Probability Bootcamp
Authors:
Aleksandar Cvetković, Phd, Mathematics
Goran S. Milovanović, Phd, Psychology
What do we want to do today?
Navigating the Intricacies of Probability Theory
In this session, we embark on a rigorous exploration of Probability
Theory, the backbone of statistical reasoning and a critical tool in the
arsenal of any data analyst. Probability Theory allows us to quantify
the uncertainty inherent in the world around us, offering a systematic
framework for making informed decisions based on incomplete information.
Starting with the fundamental concepts of probability, we will explore
the logical structure provided by Kolmogorov’s Axioms, setting the stage
for a deeper understanding of both discrete and continuous probability
distributions.
Utilizing R, we will delve into practical applications and
theoretical underpinnings of probability distributions, illustrating how
these concepts underpin much of statistical analysis. We will then
investigate the sampling distribution of the sample mean, a concept of
essential importance for understanding inference in statistics. Finally,
we’ll demystify the Central Limit Theorem, a cornerstone in statistical
theory that facilitates the understanding of distribution shapes and
sampling behaviors. This session will equip you with a robust foundation
in Probability Theory, enhancing your analytical skills and preparing
you for more complex statistical challenges.
Feedback should be send to
goran.milovanovic@datakolektiv.com.
These notebooks accompany the ADVANCED ANALYST - Foundations for
Advanced Data Analytics in R DataKolektiv
training.
Welcome to R!

0. Setup
library(tidyverse)
── Attaching core tidyverse packages ───────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ─────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
1. Event and Experimental Probability
When asked: What’s the probability of landing Tails when tossing
a (fair) coin? You’d (probably) answer: \(\frac{1}{2}\). Or 50%. But what does that
mean?
And when asked: What’s the probability of getting 6 when rolling
a six-sided (fair) die? The expected answer: \(\frac{1}{6}\). What about NOT getting
6? It’s: \(\frac{5}{6}\). And
getting a number less than five? \(\frac{2}{3}\)? But what do those numbers
mean?
And and - The probability of drawing an Ace from a (fair) deck of
cards?
Let’s get back to the coin. \(\frac{1}{2}\) says you. But what does it
mean? Maybe you meant that out of two tossings you’ll land exactly one
Tails (\(TH\) or \(HT\))? Okay, let’s toss a coin two times. I
got \(TT\). Maybe my assumption was
wrong? Or I’ve just got unlucky? Let’s toss it again. \(HT\) this time. Maybe I’m actually right?
Or I got lucky. Another two tosses! \(HH\). Hmm… Unlucky again? But can I talk
about luck in mathematics and in hypothesis testing?
What about the other answer: 50%. Does that mean that I’ll land Tails
exactly half the times when tossing a coin \(N\) times? Let’s toss it, say, 10 times.
But I don’t actually have a coin. Luckily I have R which can simulate
tossing a coin.
# - init random number generator: set.seed()
set.seed(42)
# - a coin
coin <- c('H', 'T')
# - a sample: numerical simulation of 10 coin tosses
tossing = sample(coin, size=10, replace = TRUE)
# - result
tossing
[1] "H" "H" "H" "H" "T" "T" "T" "T" "H" "T"
5 of 10 Tails. Exactly 50%! But, wait. Let’s toss the coin again!
tossing = sample(coin, size=10, replace = TRUE)
tossing
[1] "H" "T" "H" "T" "H" "H" "T" "T" "T" "T"
Hm, 6 out of 10 Tails. Ok now is this a fair coin or not?
Let’s toss a coin 100 times then.
tossing = sample(coin, size=100, replace = TRUE)
tossing
[1] "H" "H" "H" "H" "H" "T" "H" "H" "H" "H" "T" "T" "T" "T" "H" "T" "H" "T" "T" "T" "H"
[22] "H" "T" "T" "T" "T" "T" "T" "T" "H" "T" "H" "T" "T" "T" "T" "H" "T" "H" "H" "H" "T"
[43] "T" "T" "T" "T" "T" "H" "T" "H" "T" "H" "T" "T" "T" "T" "H" "H" "H" "H" "T" "H" "T"
[64] "H" "H" "T" "T" "H" "H" "H" "H" "T" "H" "T" "T" "T" "H" "T" "T" "T" "T" "T" "H" "T"
[85] "H" "T" "T" "H" "T" "H" "T" "H" "T" "H" "T" "T" "H" "H" "H" "T"
How many Tails, then?
table(tossing)
tossing
H T
44 56
56/100. Not bad. But not quite 50%. Lucky, but not as quite? (But can
I talk about luck in mathematics and in hypothesis testing?) What if
those 50% is actually an approximation of the result? Let’s
toss a coin 1000 times.
tossing = sample(coin, size=1000, replace = TRUE)
table(tossing)
tossing
H T
501 499
499/1000. That’s almost there. And that’s not that quite bad
approximation. But still… Now, let’s toss a coin ONE MILLION
TIMES.
tossing = sample(coin, size=10^6, replace = TRUE)
table(tossing)
tossing
H T
499601 500399
And that would be 0.499601 - not bad an approximation…
What if tossed a coin more than million times?
tossing = sample(coin, size=10^7, replace = TRUE)
table(tossing)
tossing
H T
4999541 5000459
Now it’s 0.4999541 - even close to 50:50. This is even better,
veeeery close to 50%.
And if we tossed a coin infinitely many times? Then
there is no approximation. We’d get 50% sharp. Mathematically, we can
write this
\[ P(T) = \lim_{N_{\rm
TRIES}\rightarrow\infty}\frac{n_{\rm HITS}}{N_{\rm TRIES}} = 0.5 =
\frac{1}{2}.\]
What are all these letters? Let’s interpret this formula one by
one.
\(T\) is the event:
event of landing Tails when tossing ONE coin.
\(P\) is the
probability: we may consider it as a function which
measures how probable an event is. So, \(P(T)\) is probability of landing Tails when
tossing one coin.
\(N_{\rm TRIES}\) is the number
of trials of the SAME experiment. In our case, it’s the number of
tossing a coin.
\(n_{\rm HITS}\) is the number
of ‘hits’, i.e. the number of desired outcomes in our set of trials. In
our case, it’s the number of Tails landed.
\(\lim_{N_{\rm
TRIES}\rightarrow\infty}\) is the limit: a value of the
fraction \(\frac{n_{\rm HITS}}{N_{\rm
TRIES}}\) which we would obtain if we would
theoretically be able to toss a coin infinitely many times in
our finite lives. But we can’t do that. We can only obtain an
experimental (or statistical) approximation of the
theoretical value in finite number of trials. And as we
demonstrated - the larger number of trials, the better the
approximation.
Let’s now illustrate this limit on another example: rolling
a six-sided die. Intuitively we say that the probability of getting a 6
is \(\frac{1}{6}\), or 16.666…% .
Similarly as with the coin, we’ll simulate rolling a die for various
numbers of trials and list the results, i.e. approximations of
theoretical probability.
# - number_of_rolls:
number_of_rolls <- round(10^seq(1.75, 7, by = 0.0525))
print(number_of_rolls)
[1] 56 63 72 81 91 103 116 131 148
[10] 167 188 213 240 271 305 345 389 439
[19] 495 559 631 712 804 907 1023 1155 1303
[28] 1471 1660 1873 2113 2385 2692 3037 3428 3868
[37] 4365 4926 5559 6273 7079 7989 9016 10174 11482
[46] 12957 14622 16501 18621 21014 23714 26761 30200 34080
[55] 38459 43401 48978 55271 62373 70388 79433 89640 101158
[64] 114156 128825 145378 164059 185140 208930 235776 266073 300262
[73] 338844 382384 431519 486968 549541 620155 699842 789769 891251
[82] 1005773 1135011 1280855 1445440 1631173 1840772 2077304 2344229 2645453
[91] 2985383 3368992 3801894 4290422 4841724 5463865 6165950 6958250 7852356
[100] 8861352 10000000
# - die <- 1:6
die <- 1:6
# - experiment
exp_prob <- lapply(number_of_rolls, function(x) {
rolls <- sample(die, x, replace = TRUE)
result <- table(rolls)
number_of_sixes = result[6]
p_of_six <- number_of_sixes/x
return(
data.frame(NumberOfRolls = x,
NumberOfSixes = number_of_sixes,
P_Six = p_of_six)
)
})
# - put the result together
exp_prob_df <- Reduce(rbind, exp_prob)
# - Show me:
print(exp_prob_df)
Let’s plot the results
ggplot(exp_prob_df,
aes(x = log(NumberOfRolls),
y = P_Six)) +
geom_line(color = "darkblue", size = .25) +
geom_point(fill = "white", color = "darkblue", size = 1) +
xlab("Number of rolls") +
ylab("Number of Sixes") +
ggtitle("Statistical Experiment: \nRolling a fair die and counting the number of sixes") +
theme_minimal() +
theme(plot.title = element_text(hjust = .5, size = 10))

From the scatterplot above we see how the values of ratio \(\frac{n_{\rm HITS}}{N_{\rm TRIES}}\)
converge towards the predicted theoretical probability of 16.666…% as
\(N_{\rm TRIES}\rightarrow\infty\).
But, is there a way to calculate theoretical probability
exactly?
2. \(\sigma\)-Algebra of Events and
Theoretical Probability
In order to be able to speak properly about theoretical probability
(and be able to compute it), we need to introduce \(\sigma\)-algebra of events and
probability-as-a-measure. We can consider \(\sigma\)-algebra of events as a family of
sets where every set is an event, and every element of this event-set is
an outcome for which consider the evenet realized, i.e. a
favorable outcome.
For example, if we toss a coin two times, then one event-set might
be
\[ A - {\rm Landed\ 1\ Heads\ and\ 1\
Tails}, \]
and its elements are
\[ A = \{HT, TH\}.\]
A set of all the possible outcomes for a given experiment/observation
is called the universal set. It is denoted by \(\Omega\) and it is the set upon which \(\sigma\)-algebra of events is built upon,
from the subsets \(A\subset\Omega\) of
the universal set.
For tossing a coin two times we have
\[ \Omega = \{HH, HT, TH,
TT\},\]
and obviously
\[ A \subset \Omega. \]
\[1^\circ\quad \emptyset,\ \Omega \in
\Sigma\]
\[2^\circ\quad A^C \in \Sigma\]
\[3^\circ\quad A\cup B \in
\Sigma\]
\[4^\circ\quad A\cap B \in
\Sigma.\]
What do these cryptic messages even mean? Let’s explain them one by
one, I promise they make sense.
\[1^\circ\quad \emptyset,\ \Omega \in
\Sigma\]
This means that the empty set \(\emptyset\) and the total set \(\Omega\) are also considered as events.
\(\emptyset\) is called an
impossible event, and it does not contain any (possible)
outcome.
\(\Omega\), viewed as an event is
called certain event - getting any outcome from all the
possible outcomes is definitely a certain event.
\[2^\circ\quad A^C \in \Sigma\]
An complementary set of \(A\): \(A^C = \Omega\setminus A\) is also
considered as an event. Every unfavourable outcome of \(A\) is favourable for \(A^C\) (and vice versa). So, for the event
\(A\) as defined above, we have:
\[ A^C = \{HH, TT\}. \]
\[3^\circ\quad A\cup B \in
\Sigma\]
A union of two events (denoted also as \(A
+ B\)) is also considered as an event. But what is a union of two
events actually? We say that the event \(A\cup
B\) is realized when at least one of the events \(A\) OR \(B\) is realized. \(A\cup B\) contains all the outcomes which
are favourable for either the event \(A\) OR event \(B\).
For example, if we define event \(B\) as
\[B - {\rm Landed\ 2\ Tails,}\]
we have
\[ A\cup B = \{HT, TH, TT\}.\]
\[4^\circ\quad A\cap B \in
\Sigma.\]
An intersection of two events (denoted also as AB) is also considered
as an event. We say that the event \(AB\) is realized when both events \(A\) AND \(B\) are simultaneously realized. \(AB\) contains all the outcomes which are
favourable for both events \(A\)
AND \(B\).
For examle, if we define event \(C\)
as
\[ C - {\rm Tails\ in\ the\ first\ coin\
toss}, \]
we have
\[ AC = \{HT, TH\} \cap \{TH, TT\} =
\{TH\}.\]
Two events \(A\) and \(B\) are mutually exclusive or
disjoint if \(A\cap B =
\emptyset\), i.e. if realization of both events simultaneously is
an impossible event.
Two events \(A\) and \(B\) are independant if the outcome
of one event does not influence the outcome of the other. For example,
when tossing a coin two times, the result of the first toss does not
influence the outcome of the second.
One more importan notion is the elementary event - an event
containing a single possible outcome. So, for tossing our coin two
times, elementary events are: \(\{HH\},\
\{HT\},\ \{TH\}\) and \(\{TT\}\).
\(\sigma\)-algebras of events serve
us as a brige between the natural language by which we describe events
and their outcomes with formal mathematical language to describe them
via sets, their elements and set operations. The upside of mathematical
objects is that it is natural to impose some measure on them, and now we
can measure the events, i.e. measure the probability of event
realization. So we can define probability-as-a-measure, that is a
mapping \(P\) from \(\sigma\)-algebra of events to interval a
set of real numbers via following set of axioms (known as
Kolmogorov Axioms):
Axiom 1: For any event \(A\) we have
\[ P(A)\geqslant 0.\]
Axiom 2: For certain event \(\Omega\) we have
\[ P(\Omega) = 1.\]
Axiom 3: For mutually exclusive events \(A_1, A_2, \ldots, A_n, \ldots\) we have
\[P\Big(\bigcup_{i=1}^{\infty}A_i\Big) =
\sum_{i=1}^{\infty}P(A_i).\]
What do those axioms tell us?
Axiom 1 means that the probability of any event has
to be some non-negative real number; in other words - we cannot have
negative probability (the same way we cannot have negative length,
surface or volume - which are also measures of some kind)
Axiom 2 says that the probability of a certain event
is 1 (or 100%). As we know that \(\Omega\), viewed as a set, is a universal
set, i.e. set that contains all the possible outcomes - this axiom also
tells us that the probability of observing any outcome of all possible
defined outcomes is equal to 1.
If we want to compute a probability of some union of mutually
exclusive events, Axiom 3 tells us that we can do that
just by summing the probabilities of every single event.
And here’s a nice reminder for \(\sigma\)-algebras and Kolmogorov
Axioms:
These axioms have very usefull
consequences; if \(A\) and \(B\) are two events, we have:
\[ 1^\circ\ P(\emptyset) = 0,
\]
\[ 2^\circ\ 0 \leqslant P(A) \leqslant 1,
\]
\[ 3^\circ\ P(A^C) = 1 - P(A),
\]
\[ 4^\circ\ A\subseteq B \Rightarrow P(A)
\leqslant P(B).\]
\[ 5^\circ\ A,\ B\ {\rm indep.}
\Rightarrow P(AB) = P(A)P(B).\]
Let’s now go over these consequences.
\[ 1^\circ\ P(\emptyset) = 0 \]
We saw that impossible event is represented by \(\emptyset\). So, this tells us that the
probability of an impossible event is 0.
\[ 2^\circ\ 0 \leqslant P(A) \leqslant 1
\]
Not only that the probability of an event is some non-negative real
number - it’s a real number belonging to the interval [0, 1]; and the
endpoints of this interval correspond to the impossible event (\(P(\emptyset) = 0\)) and certain event
(\(P(\Omega) = 1\)). The closer the
probability is to 1, the more probable is the realization of an event.
This also alows us to speak about probabilities in terms of
percents.
\[ 3^\circ\ P(A^C) = 1 - P(A)
\]
This tells us how to simply calculate probability of a complementary
event. If a probability of an event happening is 22%, then the
probability of it NOT happening is 78%.
\[4^\circ\ A\subseteq B \Rightarrow P(A)
\leqslant P(B) \]
If set \(A\) is contained in set
\(B\), i.e. if all the outcomes
favourable for event \(A\) are also
favourable for event \(B\), then event
\(A\) has smaller (or equal) chances
for realization than the event \(B\).
In other words - events with smaller set of favourable outcomes are less
probable.
\[ 5^\circ\ A,\ B\ {\rm indep.}
\Rightarrow P(AB) = P(A)P(B).\]
If two events are independant, then the probability of them both
occuring is equal to the product of probabilites of each event occuring
independantly. If we toss a coin two times, we can calculate
\[P(HT) = P(H)P(T).\]
All this talk about the Probability Theory, but we still haven’t
figured out how to calculate theoretical probability. Don’t worry, we
are almost there - and we have all the ingredients to write a formula
that stems quite naturally from the theoretical foundations above.
As we saw, probabilty of an event should be some number between 0 and
1, with probabilities of impossible and certain event as extreme values.
And the bigger the event/set is, the biger its probability should be.
This leads us to define theoretical probability of an event (\(A\)) via the following simple formula:
\[P(A) = \frac{|A|}{|\Omega|} = \frac{{\rm
No.\ of\ all\ the\ favourable\ outcomes\ for}\ A}{{\rm No.\ of\ all\
the\ possible\ outcomes}}.\]
(\(|A|\) is the cardinality
of a set, i.e. a number of elements that set has.)
One can easily check that formula for the probability, as given
above, satisfies all the Kolmogorov Axioms and, of course, all the
listed consequences.
Now that we have the ‘formula for probability’ we can easily
calculate probabilities of the events listed at the beginning of this
notebook.
- Probability of landing Tails on a coin toss:
\[P(T) = \frac{|\{T\}|}{|\{H, T\}|} =
\frac{1}{2}.\]
- Probability of landing at least one Tails on two coin tosses (event
\(A\)):
\[P(A) = \frac{|\{TH, HT, TT\}|}{|\{HH,
TH, HT, TT\}|} = \frac{3}{4}.\]
- Probability of getting 6 when rolling a six-sided die:
\[P(X = 6) = \frac{|\{6\}|}{|\{1, 2, 3, 4,
5, 6\}|} = \frac{1}{6}.\]
- Probability of getting less than 5 when rolling a six-sided
die:
\[P(X < 5) = \frac{|\{1, 2, 3,
4\}|}{|\{1, 2, 3, 4, 5, 6\}|} = \frac{4}{6} = \frac{2}{3}.\]
- Probability of getting any number from 1 to 6 when rolling a
six-sided die:
\[P(1\leqslant X\leqslant 6) = \frac{|\{1,
2, 3, 4, 5, 6\}|}{|\{1, 2, 3, 4, 5, 6\}|} = \frac{6}{6} =
1.\]
- Probability of getting a 7 when rolling a six-sided die:
\[P(X = 7) = \frac{|\emptyset|}{|\{1, 2,
3, 4, 5, 6\}|} = \frac{0}{6} = 0.\]
- Probability of getting 6 or 7 when rolling a 20-sided die:
\[P(\{X=6\}\cup\{X=7\}) = P(X=6) + P(X=7)
= \frac{1}{20} + \frac{1}{20} = \frac{2}{20} =
\frac{1}{10}.\]
- Probability of getting less than 19 when rolling a 20-sided
die:
\[P(X < 19) = 1 - P(\{X < 19\}^C) =
1 - P(X\geqslant 19) = 1 -\frac{|\{19, 20\}|}{|\{1, 2, \ldots, 20\}|} =
1 - \frac{2}{20} = \frac{18}{20} = \frac{9}{10}.\]
- Probability of getting two sixes in two dice rolls:
\[P(\{X_1 = 6\}\cap\{X_2 = 6\}) = P(\{X_1
= 6\})P(\{X_2 = 6\}) = \frac{1}{6}\cdot\frac{1}{6} =
\frac{1}{36}.\]
- Probability of drawing an Ace from a deck of cards:
cat('P(X = A) = |{\U0001F0A1, \U0001F0B1, \U0001F0C1, \U0001F0D1}|/|Whole Deck of Cards| = 4/52 = 1/13.')
P(X = A) = |{🂡, 🂱, 🃁, 🃑}|/|Whole Deck of Cards| = 4/52 = 1/13.
Even though we have a tool to calculate probability of an event
exactly, we shouldn’t forget about experimental probability. First, it
can serve us to experimentally check our theoretical calculation.
Secondly, and more important: sometimes calculating theoretical
probability is difficult, or even impossible; so, performing the
experiments and noting down the results is a way to obtain the
probability of an event.
While we’ve been using the given formula for calculating theoretical
probabilities, there was actually one important thing that we hid under
the rug - the formula works only in case when \(\Omega\) is finite set. But what if \(\Omega\) is infinite? Does all this
mathematical construction breaks down? Actually no. \(\sigma\)-algebras, Kolmogorov Axioms and
their consequences actually give us tools to handle even infinite \(\Omega\)s, and we’ll see how later on.
But first let’s talk about random variables.
3. Random variable: a Discrete Type
When we were calculating theoretical probabilities a bit above we
introduced the following notation \(P(X =
6)\) or \(P(X \geqslant 19)\).
What is this \(X\)? It is actually a
random variable (RV) - a varable which can take one of the several
different values, each with its own probability.
There are two types of random variables: discrete and
(absolutely) continuous. Values of RV are elements,
i.e. outcomes of set \(\Omega\). If
\(\Omega\) is finite or countably
infinite, then associated RV is discrete. If \(\Omega\) is uncountably infinite,
then the corresponding RV is continuous.
We’ll speak about discrete RVs now.
A discrete random variable is fully described if we know all the
values it can take, and all the probabilities corresponding to those
values. This ‘description’ of a random variable is called its
distribution. So, ‘knowing’ a random variable is equivalent to
‘knowing’ its distribution.
For tossing a coin, a random variable \(X\) can take two values: Heads and Tails,
each with probability \(\frac{1}{2}\).
We can write its distribution as
\[ X :
\begin{pmatrix}
H & T\\
\frac{1}{2} & \frac{1}{2}
\end{pmatrix}.\]
This ‘function’ which assigns a probability to each outcome is also
called probability mass function or p.m.f.
For rolling a six sided die, we have a random variable which can take
some of the values from the die given with p.m.f
\[ X :
\begin{pmatrix}
1 & 2 & 3 & 4 & 5 & 6\\
\frac{1}{6} & \frac{1}{6} & \frac{1}{6} & \frac{1}{6} &
\frac{1}{6} & \frac{1}{6}
\end{pmatrix}.\]
We can even define RVs on our own. The only important thing is that
all the probabilites in its distribution need to sum to 1 (as the union
of all elementary events, i.e. of every possible outcome gives \(\Omega\), and \(P(\Omega) = 1\)).
For example:
\[ X :
\begin{pmatrix}
-1 & 0 & 2 & 4.7\\
0.2 & 0.05 & 0.45 & 0.3
\end{pmatrix}.\]
We can simulate the outcomes of this RV using
sample():
choices <- c(-1, 0, 2, 4.6)
sample(choices, size = 1, prob = c(.2, .05, .45, .3))
[1] 0
choices <- c(-1, 0, 2, 4.6)
result <- sample(choices, size = 100, prob = c(.2, .05, .45, .3), replace = TRUE)
result
[1] 2.0 2.0 2.0 -1.0 -1.0 -1.0 2.0 2.0 2.0 2.0 -1.0 4.6 2.0 2.0 4.6 2.0
[17] 2.0 4.6 -1.0 4.6 4.6 -1.0 2.0 4.6 4.6 -1.0 4.6 4.6 2.0 4.6 -1.0 4.6
[33] 4.6 2.0 2.0 4.6 0.0 0.0 -1.0 -1.0 -1.0 4.6 4.6 4.6 4.6 -1.0 -1.0 0.0
[49] 0.0 -1.0 2.0 2.0 2.0 -1.0 4.6 2.0 4.6 2.0 2.0 -1.0 -1.0 4.6 4.6 2.0
[65] 4.6 4.6 4.6 -1.0 4.6 0.0 2.0 2.0 4.6 4.6 2.0 2.0 2.0 2.0 2.0 -1.0
[81] 2.0 2.0 2.0 4.6 4.6 4.6 2.0 -1.0 -1.0 0.0 4.6 4.6 4.6 2.0 2.0 2.0
[97] 2.0 -1.0 2.0 2.0
result_frame <- as.data.frame(table(result))
result_frame
ggplot(result_frame,
aes(x = result,
y = Freq,
color = result,
fill = result)) +
geom_bar(stat = "identity") +
xlab("Values") + ylab("Frequency") +
theme_minimal() +
ggtitle("Statistical Experiment\nSome discrete distribution that I have made up") +
theme(plot.title = element_text(hjust = .5, size = 10)) +
theme(legend.position = "none")

Let’s assume we have a following discrete random variable which can
take infinitely many values:
\[ X :
\begin{pmatrix}
1 & 2 & \cdots & k & \cdots \\
p_1 & p_2 & \cdots & p_k & \cdots &
\end{pmatrix}.\]
Using its distribution, we can calculate probabilities of some events
in the following manner (which also applies for finite RVs):
\[ P(X = k) = p_k \]
\[ P(X \leqslant k) = p_1 + p_2 + \cdots +
p_k \]
\[ P(X < k) = P(X \leqslant k - 1) =
p_1 + p_2 + \cdots + p_{k-1} \]
\[ P(3 \leqslant X \leqslant k) = p_3 +
p_4 + \cdots + p_k \]
\[ P(X > k) = 1 - P(X \leqslant k) = 1
- (p_1 + p_2 + \cdots + p_k) \]
Probability \(P(X\leqslant k)\) is
quite an important one - it even has it’s name cumulative
distribution function or c.d.f.. It’s denoted by \(F(k)\), and it simply sums all the
probabilities for all values up to \(k\):
\[ F :
\begin{pmatrix}
1 & 2 & 3 & \cdots & N\\
p_1 & p_1 + p_2 & p_1 + p_2 + p_3 & \cdots & 1
\end{pmatrix}.\]
4. Discrete Type Distributions
Random or stochastic processes are not just some
mathematical intellectual plaything. They occur all around us and within
us. Many stochastic natural and social phenomena behave according to
some distribution, and can be modelled according to that distribution.
Here we go through some of those important discrete-type
distributions.
Bernoulli Distribution
The simplest discrete distribution is Bernoulli Distribution - it’s a
distribution of a RV that has binary outcomes - yes/no, hit/miss or
success/failure. The parameter of this distribution is \(p\) - the probability of ‘hit’. This
distribution looks like:
\[ X :
\begin{pmatrix}
0 & 1\\
1 - p & p
\end{pmatrix}.\]
To sample from Bernoulli Distribution we can use
sample() with two outcomes. Say, we have a population that
gives a ‘yes’ answer to some question in 2/3 cases and ‘no’ in 1/3
cases, and we want to draw a sample of 100 from it. Here’s a sample:
X = c('yes', 'no')
no_tries = 100
outcomes = sample(X, prob = c(2/3, 1/3), size = no_tries, replace = TRUE)
no_outcomes = as.data.frame(table(outcomes))
ggplot(no_outcomes,
aes(x = outcomes,
y = Freq,
color = outcomes,
fill = outcomes)) +
geom_bar(stat = "identity") +
xlab("Outcomes") + ylab("Frequency") +
theme_minimal() +
ggtitle("Bernoulli's Statistical Experiment") +
theme(plot.title = element_text(hjust = .5, size = 10)) +
theme(legend.position = "none")

Binomial Distribution
Binomial distribution gives an answer to the following question:
what’s the probability of getting \(k\) hits in \(n\) trials of the same hit/miss experiment,
having probability of hit equal to \(p\)?
Binomial Distribution has two parameters:
- \(n\): number of repetitions of the
same experiment
- \(p\): probability of a hit
(success)
If a random variable \(X\) has a
Binomial Distribution with parameters \(n\) and \(p\), we write that
\[ X \sim \mathcal{B}(n, p).\]
For example, we’d like to know what’s the probability of landing 7
Heads in tossing a coin 10 times. We can approximate this probability by
sampling from the Binomial Distribution with parameters \(n=10\) and \(p=0.5\) (\(\mathcal{B}(100, 0.5)\)), using
rbinom().
outcomes = rbinom(n = 100, prob = .5, size = 10)
outcomes
[1] 7 5 4 6 4 3 6 6 8 5 7 6 7 4 5 8 0 9 5 5 6 7 4 5 6 6 3 5 5 4 7 4 9 4 6 5 4 4 2 8 3 8
[43] 7 3 1 4 4 5 7 7 6 5 4 6 5 5 5 7 2 5 7 5 5 6 4 6 4 4 4 5 6 7 7 6 8 5 3 4 4 6 6 4 6 5
[85] 6 5 7 6 7 4 6 6 7 4 3 5 5 5 5 6
no_outcomes = as.data.frame(table(outcomes))
ggplot(no_outcomes,
aes(x = outcomes,
y = Freq)) +
geom_bar(stat = "identity", color = "darkblue", fill = "white") +
xlab("Outcomes") + ylab("Frequency") +
theme_minimal() +
ggtitle("Binomial Statistical Experiment") +
theme(plot.title = element_text(hjust = .5, size = 10)) +
theme(legend.position = "none")

We can actually calculate theoretical Binomial probabilities, via
\[P(X = k) =
\binom{n}{k}p^k(1-p)^{n-k},\]
where \(\binom{n}{k}\), called
binomial coefficient, is
\[\binom{n}{k} =
\frac{n!}{k!(n-k)!}.\]
Say we have some unfair coin which lands Tails with probability 0.25,
and we toss it three times. Using the formula above for \(\mathcal{B}(3, 0.25)\) we obtain the
probabilities for number of Tails in 3 tosses \(X\):
\[ X :
\begin{pmatrix}
0 & 1 & 2 & 3\\
0.421875 & 0.421875 & 0.140625 & 0.015625
\end{pmatrix}.\]
Interlude: Functions to work with probability in R
Consider the following experiment: a person rolls a fair dice ten
times. Q. What is the probability of obtaining five or
less sixes at random?
We know that R’s dbinom() represents the binomial
probability mass function (p.m.f.). Let’s see: the probability of
getting exactly five sixes at random is:
pFiveSixes <- dbinom(5, size = 10, p = 1/6)
pFiveSixes
[1] 0.01302381
Do not be confused by our attempt to model dice rolls by a binomial
distribution: in fact, there are only two outcomes here, “6 is obtained”
with \(p=1/6\) and “everything else”
with \(1−p=5/6\)!
Then, the probability of getting five or less than five sixes from
ten statistical experiments is:
pFiveAndLessSixes <- sum(
dbinom(0, size = 10, p = 1/6),
dbinom(1, size = 10, p = 1/6),
dbinom(2, size = 10, p = 1/6),
dbinom(3, size = 10, p = 1/6),
dbinom(4, size = 10, p = 1/6),
dbinom(5, size = 10, p = 1/6)
)
pFiveAndLessSixes
[1] 0.9975618
in order to remind ourselves that the probabilities of all outcomes
from a discrete probability distribution - in our case, that “0 sixes”,
“1 six”, “2 sixes”, “3 sixes”, “4 sixes”, or “5 sixes” etc. obtain -
will eventually sum up to one. However, let’s wrap this up elegantly by
using sapply()
pFiveAndLessSixes <- sum(sapply(seq(0,5), function(x) {
dbinom(x, size = 10, p = 1/6)
}))
pFiveAndLessSixes
[1] 0.9975618
or, even better, by recalling that we are working with a vectorized
programming language:
pFiveAndLessSixes <- sum(dbinom(seq(0,5), size = 10, p =1/6))
pFiveAndLessSixes
[1] 0.9975618
Of course, we could have used the cumulative distribution
function* (c.d.f) to figure out this as well:
pFiveAndLessSixes <- pbinom(5, size = 10, p = 1/6)
pFiveAndLessSixes
[1] 0.9975618
Random Number Generation from the Binomial
rbinom() will provide a vector of random deviates from
the Binomial distribution with the desired parameter, e.g.:
# Generate a sample of random binomial variates:
randomBinomials <- rbinom(n = 100, size = 1, p = .5)
randomBinomials
[1] 1 0 1 0 0 0 1 0 1 0 0 1 1 1 0 1 1 0 0 0 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 1 1 0 0 0 1
[43] 0 0 1 0 0 1 0 0 1 0 0 0 0 1 1 0 0 1 1 0 0 0 1 1 1 0 0 0 1 0 1 1 1 0 1 0 1 0 0 0 1 1
[85] 1 0 0 0 1 1 0 0 0 1 0 1 1 0 0 1
Now, if each experiment encompasses 100 coin tosses:
randomBinomials <- rbinom(n = 100, size = 100, p = .5)
randomBinomials # see the difference?
[1] 46 52 50 42 53 51 49 43 45 49 57 52 61 61 58 49 51 49 42 46 45 48 48 49 50 48 53 49
[29] 52 50 46 55 49 50 53 50 52 48 51 45 48 55 61 49 53 53 46 42 56 57 47 50 46 51 42 48
[57] 60 46 53 51 48 46 47 50 44 49 60 51 46 46 49 47 51 47 40 51 48 48 49 55 50 48 53 48
[85] 50 54 53 49 43 65 48 51 45 50 53 47 44 51 44 50
randomBinomials <- rbinom(n = 100, size = 10000, p = .5)
randomBinomials
[1] 4935 5020 5015 5064 5007 4988 4949 5010 5042 4924 5041 5021 5000 4992 4922 5058
[17] 4969 4888 5105 4960 5038 5020 4923 5028 4920 4977 4997 4972 4955 5039 5104 5002
[33] 5024 4937 4986 5063 5038 4974 4968 4984 4981 5064 4972 5004 5037 5055 4950 4952
[49] 4996 4991 5089 5054 4981 4985 5000 4939 5032 5010 4989 4998 4976 5058 5140 5008
[65] 4929 5057 4972 4968 4923 5053 4975 4983 5043 4991 5053 5022 5084 5052 5028 4941
[81] 5072 5052 5001 4999 5008 5038 4979 5050 4959 5053 4956 5043 5059 5043 5099 4971
[97] 4955 4967 5023 5052
Let’s plot the distribution of the previous experiment:
randomBinomialsPlot <- data.frame(success = randomBinomials)
ggplot(randomBinomialsPlot,
aes(x = success)) +
geom_histogram(binwidth = 10,
fill = 'white',
color = 'darkblue') +
theme_minimal() +
theme(panel.border = element_blank())

Interpretation: we were running 100 statistical experiments, each
time drawing a sample of 10000 observations of a fair coin (\(p=.5\)). And now,
randomBinomials <- rbinom(100000, size = 100000, p = .5)
randomBinomialsPlot <- data.frame(success = randomBinomials)
ggplot(randomBinomialsPlot,
aes(x = success)) +
geom_histogram(binwidth = 10,
fill = 'white',
color = 'darkblue') +
theme_minimal() +
theme(panel.border = element_blank())

… we were running 10000 statistical experiments, each time drawing a
sample of 100000 observations of a fair coin (\(p=.5\)).
So, we have the Probability Mass Function (p.m.f):
heads <- 0:100
binomialProbability <- dbinom(heads, size = 100, p = .5)
sum(binomialProbability)
[1] 1
Where sum(binomialProbability) == 1 is TRUE
because this is a discrete distribution so its Probability Mass
Functions outputs probability indeed!
binomialProbability <- data.frame(heads = heads,
density = binomialProbability)
ggplot(binomialProbability,
aes(x = heads,
y = density)) +
geom_bar(stat = "identity",
fill = 'white',
color = 'darkblue') +
ggtitle("Binomial P.M.F.") +
theme_minimal() +
theme(panel.border = element_blank()) +
theme(plot.title = element_text(hjust = .5, size = 10))

The Cumulative Distribution Function (c.d.f), on the other hand:
heads <- 1:100
binomialProbability <- pbinom(heads, size = 100, p = .5)
sum(binomialProbability)
[1] 51
binomialProbability <- data.frame(heads = heads,
cumprob = binomialProbability)
ggplot(binomialProbability,
aes(x = heads,
y = cumprob)) +
geom_bar(stat = "identity",
fill = 'white',
color = 'darkblue') +
ylab("P(heads <= x)") +
ggtitle("Binomial C.D.F.") +
theme_minimal() +
theme(panel.border = element_blank()) +
theme(plot.title = element_text(hjust = .5, size = 10))

Quantile Function of the Binomial distribution
The quantile is defined as the smallest value \(x\) such that \(F(x)\geq p\), where \(F\) is the cumulative distribution function
(c.d.f.):
qbinom(p = .01, size = 100, prob = .5)
[1] 38
qbinom(p = .99, size = 100, prob = .5)
[1] 62
qbinom(p = .01, size = 200, prob = .5)
[1] 84
qbinom(p = .99, size = 200, prob = .5)
[1] 116
Similarly, we could have obtained \(Q_1\), \(Q_3\), and the median, for say n of 100
(that is size in qbinom()) and \(p\) of \(.5\) (that is prob in
qbinom()):
qbinom(p = .25, size = 100, prob = .5)
[1] 47
qbinom(p = .5, size = 100, prob = .5)
[1] 50
qbinom(p = .75, size = 100, prob = .5)
[1] 53
qbinom(p = .25, size = 1000, prob = .5)
[1] 489
Here are some examples of the binomial distribution in nature and
society:
Coin Flips: When flipping a fair coin multiple times, the number
of heads obtained follows a binomial distribution. Each flip is an
independent trial with two outcomes (heads or tails) and an equal
probability of success (0.5 for a fair coin).
Product Quality Control: In manufacturing processes, the number
of defective items in a production batch can often be modeled using a
binomial distribution. Each item is inspected independently, and it is
classified as either defective or non-defective based on predetermined
criteria.
Election Outcomes: In a two-party political system, the
distribution of seats won by each party in a series of elections can be
modeled using a binomial distribution. Each election is treated as an
independent trial with two possible outcomes (win or loss) and a fixed
probability of success for each party.
Medical Trials: Clinical trials often involve testing the
effectiveness of a treatment or medication on a group of patients. The
binomial distribution can be used to model the number of patients who
respond positively to the treatment, where each patient is considered a
trial with a fixed probability of response.
Sports Performance: The success rate of an athlete in a specific
type of action, such as free throws in basketball or penalty kicks in
soccer, can be modeled using a binomial distribution. Each attempt
represents an independent trial with two outcomes (success or failure)
and a fixed probability of success for the athlete.
Genetic Inheritance: The transmission of certain genetic traits
from parents to offspring can be modeled using a binomial distribution.
For example, the probability of a child inheriting a specific allele
from a heterozygous parent follows a binomial distribution.
Survey Responses: In opinion polls or market research surveys,
the distribution of responses to a yes-or-no question can be modeled
using a binomial distribution. Each participant provides a single
response, which can be categorized as either a success (yes) or failure
(no) outcome.
Poisson Distribution
Poisson is a discrete probability distribution with mean
and variance correlated. This is the distribution of the number of
occurrences of independent events in a given interval.
The p.m.f. is given by:
\[{P(X=k)} =
\frac{\lambda^{k}e^{-\lambda}}{k!}\] where \(\lambda\) is the average number of events
per interval, and \(k = 0, 1,
2,...\)
For the Poisson distribution, we have that the mean (the expectation)
is the same as the variance:
\[X \sim Poisson(\lambda) \Rightarrow
\lambda = E(X) = Var(X) \]
Example. (Following and adapting from Ladislaus Bortkiewicz,
1898). Assumption: on the average, 10 soldiers in the Prussian army were
killed accidentally by horse kick monthly. What is the probability that
17 or more soldiers in the Prussian army will be accidentally killed by
horse kicks during the month? Let’s see:
tragedies <- ppois(16, lambda=10, lower.tail=FALSE) # upper tail (!)
tragedies
[1] 0.02704161
Similarly as we have used pbinom() to compute cumulative
probability from the binomial distribution, here we have used
ppois() for the Poisson distribution. The
lower.tail=F argument turned the cumulative into a
decumulative (or survivor) function: by calling
ppois(17, lambda=10, lower.tail=FALSE) we have asked not
for \(P(X \leq k)\), but for \(P(X > k)\) instead. However, if this is
the case, than our ppois(16, lambda=10) answer would be
incorrect, and that is why we called:
ppois(16, lambda=10, lower.tail=FALSE) instead. Can you see
it? You have to be very careful about how exactly your probability
functions are defined (c.f. Poisson {stats} documentation
at (https://stat.ethz.ch/R-manual/R-devel/library/stats/html/Poisson.html)
and find out whether lower.tail=T implies \(P(X > k)\) or \(P(X \geq k)\)).
# Compare:
tragedies <- ppois(17, lambda=10, lower.tail=TRUE) # lower tail (!)
tragedies
[1] 0.9857224
This ^^ is the answer to the question of what would be the
probability of 17 and and less than 17 deaths.
The same logic to generate random deviates as we have observed in the
Binomial case is present here; we have rpois():
poissonDeviates <- rpois(100000,lambda = 5)
poissonDeviates <- data.frame(events = poissonDeviates)
ggplot(poissonDeviates,
aes(x = events)) +
geom_histogram(binwidth = 1,
fill = 'white',
color = 'darkorange') +
ggtitle("Poisson Distribution") +
theme_minimal() +
theme(panel.border = element_blank()) +
theme(plot.title = element_text(hjust = .5, size = 10))

Observe how the shape of the Poisson distribution changes with its
mean and variance, both represented as \(\lambda\):
lambda <- 1:20
poissonDeviates <- lapply(lambda, rpois, n = 1000)
poissonDeviates <- reduce(poissonDeviates, rbind)
dim(poissonDeviates)
[1] 20 1000
Ok, then:
poissonDeviates <- as.data.frame(poissonDeviates)
poissonDeviates$id <- 1:dim(poissonDeviates)[1]
poissonDeviates <- poissonDeviates %>%
pivot_longer(cols = -id) %>%
select(-name)
poissonDeviates$id <- factor(poissonDeviates$id,
levels = sort(unique(poissonDeviates$id)))
and finally:
ggplot(poissonDeviates,
aes(x = value)) +
geom_histogram(binwidth = 1,
fill = 'white',
color = 'darkorange') +
xlab("Events") +
ggtitle(expression(lambda)) +
facet_wrap(~id, scales = "free_x") +
theme_minimal() +
theme(panel.border = element_blank()) +
theme(strip.background = element_blank()) +
theme(strip.text = element_text(size = 12)) +
theme(plot.title = element_text(hjust = .5, size = 20))

Let’s study one important property of the Poisson distribution:
lambda <- 1:100
poissonMean <- sapply(lambda, function(x) {
mean(rpois(100000,x))
})
poissonVar <- sapply(lambda, function(x) {
var(rpois(100000,x))
})
poissonProperty <- data.frame(mean = poissonMean,
variance = poissonVar)
ggplot(poissonProperty,
aes(x = mean,
y = variance)) +
geom_line(color = "darkorange", size = .25) +
geom_point(color = "darkorange", fill = "darkorange", size = 1.5) +
xlab("E(X)") + ylab("Var(X)") +
ggtitle("Poisson Distribution\nThe Mean is equal to Variance") +
theme_minimal() +
theme(panel.border = element_blank()) +
theme(plot.title = element_text(hjust = .5, size = 10))

Here are some examples of how the Poisson distribution can be
observed in various contexts:
Natural Disasters: The occurrence of earthquakes, volcanic
eruptions, or meteorite impacts can be modeled using a Poisson
distribution. While these events are rare, they tend to follow a pattern
of random occurrence over time.
Traffic Accidents: The frequency of traffic accidents at a
particular intersection or along a specific road can often be
approximated by a Poisson distribution. The assumption is that accidents
happen randomly, and the number of accidents in a given time period
follows a Poisson distribution.
Phone Calls to a Call Center: The number of incoming phone calls
to a call center during a specific time interval can often be modeled
using a Poisson distribution. Even though call volumes may vary, they
tend to follow a pattern of random occurrence throughout the
day.
Radioactive Decay: The decay of radioactive particles follows a
Poisson distribution. Each particle has a fixed probability of decaying
within a given time period, and the number of decays within that time
period follows a Poisson distribution.
Birth and Death Rates: In population studies, the number of
births or deaths in a particular region over a fixed time period can be
approximated using a Poisson distribution. While births and deaths may
exhibit seasonal or long-term trends, the occurrences within short
intervals can be modeled as random events.
Email Arrival: The arrival of emails in an inbox can often be
modeled using a Poisson distribution. The assumption is that emails
arrive randomly, and the number of emails received within a fixed time
period can follow a Poisson distribution.
Molecular Collision Events: In physics and chemistry, the number
of collision events between molecules within a given volume can be
described using a Poisson distribution. This applies to scenarios such
as gas diffusion or the movement of particles in a fluid.
Network Traffic: The number of packets arriving at a network
router or the number of requests received by a web server within a
specific time interval can often be modeled using a Poisson
distribution. This is particularly useful in analyzing network
congestion and capacity planning.
Defective Products: The number of defective products in a
manufacturing process can be modeled using a Poisson distribution. It
helps assess the quality control measures and estimate the likelihood of
encountering defective items in a production batch.
Hospital Admissions: The number of patients admitted to a
hospital within a given time period can often be approximated by a
Poisson distribution. This information aids in resource allocation and
staffing decisions.
Rare Disease Occurrence: The occurrence of rare diseases within a
population can be modeled using a Poisson distribution. This
distribution helps estimate the likelihood of rare diseases and evaluate
the effectiveness of preventive measures or treatments.
Natural Birth Intervals: The time intervals between births in
animal populations, such as a particular species of birds, can follow a
Poisson distribution. This helps researchers understand reproductive
patterns and population dynamics.
Insurance Claims: The number of insurance claims filed within a
specific time period, such as automobile accidents or property damage
claims, can often be modeled using a Poisson distribution. This assists
insurers in risk assessment and pricing policies.
Arrival of Customers: The arrival of customers at a retail store
or the number of customers in a queue at a bank can be approximated
using a Poisson distribution. This information aids in optimizing
service capacity and wait time estimation.
Molecular Events in Genetics: The occurrence of genetic mutations
or recombination events in a DNA sequence can be modeled using a Poisson
distribution. This helps researchers study genetic variations and
evolutionary processes.
5 Random variable: a Continuous Type
We spoke about RVs that can take values from universal set \(\Omega\) which is either finit or countably
infinite; these were discrete-type random variables. But what if \(\Omega\) is uncountably infinite,i.e. what
if random variable \(X\) can take any
real number as a value? Then we have (absolutely) continuous
random variable.
As with discrete-type RVs, continuous-type RVs have their
distribution. With discrete RVs we use probability mass function (p.m.f)
to describe their distribution. With continuous RVs, we have
probability density function (p.d.f) which ‘describes’ them.
One p.d.f. \(\varphi(x)\) of a
continuous random variable may look like this:
Each p.d.f of a
continuous RV should be defined, continuous and non-negative almost
everywhere on the whole real-number line, and:
\[\int_{-\infty}^{+\infty}\varphi(x)dx =
1.\]
Don’t worry about the integral - we are not going to compute
integrals in this course. But we do need them to calculate probabilities
for continuous RVs. The integral above tells us that the area under the
curve of p.d.f. is always equal to 1. This seems familiar? This is
actually completely analogous to the fact that, for discrete RV, all
probabilities in its p.m.f. need always to sum to 1.
So, what’s the probability of RV, having p.d.f as in the figure
above, to take value -5.5? It’s
\[P(X = -0.5) = 0.\]
OK, that kinda makes sense. But, what about the probability of taking
value 0?
\[P(X = 0) = 0.\]
Wait, what? And taking value \(\sqrt{2}\)?
\[P(X = \sqrt{2}) = 0.\]
Is it always going to be zero? Yes. Because the probability of
hitting a point exactly you want, out of uncountably infinitely many
others is - of course, zero. But how we can calculate probabilities at
all, if the probability of every point is zero? We just need to broaden
our views - not to ask about the probability of hitting a single point
\(x\) among ridiculously infinitely
many, but to ask about the probability of hitting any point in the small
interval \(\Delta x\) which contains
\(x\)… and uncountably infinite many
other points. When working with continuous RVs we can hope to get
positive probability only if we work with intervals, no matter how big
or small.
However, to get positive probabilities, those intervals need to be
either fully or partially on the support of p.d.f. Support is
the part of p.d.f. curve where this function is positive, i.e. \(\varphi(x) > 0\). For really small
intervals \(\Delta x\) that are outside
of the support, i.e. for which \(\varphi(x) =
0\), there is 0 probability for RV \(X\) to take any value from that interval.
On the other hand, if we observe a really small interval \(\Delta x\) around the point where p.d.f.
takes its maximal value - there is the highest probability of RV \(X\) taking values from this interval.
In other words: higher the value of \(\varphi(x)\) for some \(x\) - higher the probability of RV \(X\) getting some value from a very small
interval \(\Delta x\) around \(x\). (but not in \(x\) itself; there, the probability is
zero)
So, we can calculate probability of continuous RV in any interval.
How do we calculate probability of RV \(X\) taking values in some interval \([a, b]\) where \(a\) and \(b\) are two numbers? Simply, using the
formula:
\[P(a \leqslant X \leqslant b) =
\int_a^b\varphi(x)dx.\]
It’s that integral again… And again it makes sense again. Remember
that, using Kolmogorov Axioms, we defined probability as a measure? We
also mentioned that integral is the measure of the area under the curve.
So, using an integral to measure area under the curve of p.d.f on some
interval \([a,b]\), we are actually
measuring the probability of RV \(X\)
to take any value from that interval.
Observing probabilites as integrals, i.e. areas of surfaces under the
p.d.f curve is completely in-sync with Kolmogorov Axioms for
probability-as-a-measure:
- Both probability and area are measures, and cannot be negative;
- Area of an empty set, or a point/line is zero. So is the
probability;
- Bigger the area, bigger the probability; and we may encompass big
areas by taking long intervals or intervals around high values of
p.d.f;
- We can just sum areas of disjoint figures to get the total area; the
same is for probabilities of disjoint events.
One more important point. We saw that for discrete RVs every point
matters (even if it’s infinite). For some discrete RV \(X\) we have
\[P(X \leqslant k) \neq P(X <
k).\]
However, this is not the case with continuous RVs; there one point
doesn’t make a difference (why?). So, for some continuous RV \(X\) we have
\[P(a \leqslant X \leqslant b) = P(a
\leqslant X < b) = P(a < X < b).\]
As discrete RVs have c.d.f, so continuous RVs have one as well.
Cumulative distribution function for a continuous random variable is
given with
\[F(a) = P(X\leqslant a) =
\int_{-\infty}^a\varphi(x)dx.\]
C.d.f of a continuous RV \(X\) gives
the probability of \(X\) taking any
value from the interval \((-\infty,
a]\). Or, area under the p.d.f curve on that interval. Here’s how
one c.d.f. of a continuous RV looks like:
C.d.f. is a prety handy tool for computing
probabilities of continuous RVs, even more useful than p.d.f! In fact,
by knowing the c.d.f we can compute probabilites while avoiding
integrals, using this formula:
\[P(a \leqslant X \leqslant b) = F(b) -
F(a).\]
And we can also calculate
\[P(X \geqslant a) = 1 - P(X < a) = 1 -
F(a).\]
For continuous RVs very useful is quantile function, which
is the inverse of a c.d.f:
\[Q(p) = F^{-1}(p),\qquad p\in(0,
1).\]
Simply put, for \(p = 0.1\) we can
get a value \(x\) for which we can
expect to find 10% of points in the interval \((-\infty, x]\) when sampling from a RV
\(X\) having c.d.f \(F\).
The most important values for \(p\)
for the quantile functions are 0.25, 0.5 and 0.75, which give values for
quartiles Q1, Q2 (median) and Q3.
Normal (or Gaussian) distribution
Normal Distribution, denoted by \(\mathcal{N}(\mu, \sigma^2)\), has two
parameters:
\(\mu\): mean, which
dictates the peak of the bell-curve, i.e. a point which neighbourhood
should be most densly populated by samples;
\(\sigma\): standard
deviation, which dictates the thickness of the bell-curve, i.e. the
dispersion of the sample away from the mean.
\(\sigma^2\) which appears in the
notation above is called variance.
The p.d.f for RV with Normal Distribution \(X\sim\mathcal{N}(\mu, \sigma^2)\) is given
by
\[\varphi(x) =
\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{1}{2}\big(\frac{x-\mu}{\sigma}\big)^2}.\]
A very special Normal Distribution is the one with \(\mathcal{N}(0, 1)\), and it’s called
Standard Normal Distribution. Its p.d.f. and c.d.f. are
\[\varphi(z) =
\frac{1}{\sqrt{2\pi}}e^{-\frac{z^2}{2}}\]
\[\Phi(z) =
\frac{1}{\sqrt{2\pi}}\int_{-\infty}^ze^{-\frac{x^2}{2}}dx.\]
It’s integrals again… shouldn’t c.d.f relieve us of calculating
integrals? Well, yes, but there’s no simpler way to represent c.d.f for
Standard Normal Distribution. Luckily, every statistics and probability
textbook has a table of values of \(\Phi(z)\) for various values of \(z\).
Looking at one of those tables we obtain, for \(Z\sim\mathcal{N}(0, 1)\),
\[\Phi(-2.27) = P(Z < -2.27) =
0.0116.\]
Example
Average time for delivery service to deliver food is 30 minutes,
with standard deviance of 10 minutes. Asumming the time for delivery is
random variable with Normal Distribution, find probability of your order
arriving more than five minutes late than average.
We have \(X\sim\mathcal{N}(30,
100)\) and calculate
\[P(X > 35) = 1 - P(X\leqslant
35).\]
But wait, we don’t have c.d.f. for this Normal Distribution, because
it’s not standard. Worry not, we can standardize a RV \(X\sim\mathcal{N}(\mu, \sigma)\) by applying
the following transformation
\[Z = \frac{X - \mu}{\sigma}.\]
RV \(Z\) has the Standard Normal
Distribution, and we can finde the value of its c.d.f. form the table.
So, we continue our calculation:
\[P(X > 35) = 1 - P(X\leqslant 35) = 1
- P\Big(\frac{X - 30}{10}\leqslant \frac{35 - 30}{10}\Big) = 1 -
P(Z\leqslant 0.5) = 1 - \Phi(0.5) = 1 - 0.6915 = 0.3085.\]
Now, for statistical experiments with continuous outcomes, such as
those modeled by the Normal Distribution, the things are different than
with discrete R.V.s. Recall that P(X == x) - the
probability of some exact, real value - is always
zero. That is simply the nature of the continuum. What we can obtain
from continuous probability functions is a probability that some value
falls in some precisely defined interval. If we would want to do that
from a Probability Density Function, we would need to integrate
that function across that interval. But that is probably what you do not
want to do, because there is a way simpler approach, illustrated with
pbinom() in the example with the Binomial experiment above.
Assume that we are observing people in a population with an average
height of 174 cm, with the standard deviation of 10 cm. What is the
probability to randomly meet anyone who is less than (or equal) 180 cm
tall?
pnorm(180, mean = 174, sd = 10)
[1] 0.7257469
And what is the probability to randomly observe a person between 160
cm and 180 cm?
pnorm(180, mean = 174, sd = 10) - pnorm(160, mean = 174, sd = 10)
[1] 0.6449902
That is how you obtain the probability of real-valued, continuous
statistical outcomes. Let’s plot the Probability Density and the
Cumulative Distribution functions for this statistical experiment.
Density first:
observations <- 1:300
normalProbability <- dnorm(observations, mean = 179, sd = 10)
normalProbability
[1] 6.309257e-71 3.722639e-70 2.174607e-69 1.257672e-68 7.201308e-68 4.082370e-67
[7] 2.291239e-66 1.273167e-65 7.004182e-65 3.814930e-64 2.057182e-63 1.098287e-62
[13] 5.805189e-62 3.037902e-61 1.573940e-60 8.073459e-60 4.100041e-59 2.061454e-58
[19] 1.026163e-57 5.057269e-57 2.467589e-56 1.192029e-55 5.701085e-55 2.699513e-54
[25] 1.265524e-53 5.873709e-53 2.699054e-52 1.227913e-51 5.530710e-51 2.466330e-50
[31] 1.088876e-49 4.759516e-49 2.059701e-48 8.824755e-48 3.743331e-47 1.572066e-46
[37] 6.536427e-46 2.690711e-45 1.096607e-44 4.424780e-44 1.767622e-43 6.991082e-43
[43] 2.737514e-42 1.061269e-41 4.073348e-41 1.547870e-40 5.823376e-40 2.169062e-39
[49] 7.998828e-39 2.920369e-38 1.055616e-37 3.777736e-37 1.338487e-36 4.695195e-36
[55] 1.630611e-35 5.606657e-35 1.908599e-34 6.432540e-34 2.146384e-33 7.090703e-33
[61] 2.319147e-32 7.509729e-32 2.407561e-31 7.641655e-31 2.401345e-30 7.471002e-30
[67] 2.301231e-29 7.017760e-29 2.118819e-28 6.333538e-28 1.874372e-27 5.491898e-27
[73] 1.593111e-26 4.575376e-26 1.300962e-25 3.662345e-25 1.020731e-24 2.816567e-24
[79] 7.694599e-24 2.081177e-23 5.573000e-23 1.477495e-22 3.878112e-22 1.007794e-21
[85] 2.592865e-21 6.604580e-21 1.665588e-20 4.158599e-20 1.027977e-19 2.515806e-19
[91] 6.095758e-19 1.462296e-18 3.472963e-18 8.166236e-18 1.901082e-17 4.381639e-17
[97] 9.998379e-17 2.258809e-16 5.052271e-16 1.118796e-15 2.452855e-15 5.324148e-15
[103] 1.144156e-14 2.434321e-14 5.127754e-14 1.069384e-13 2.207990e-13 4.513544e-13
[109] 9.134720e-13 1.830332e-12 3.630962e-12 7.131328e-12 1.386680e-11 2.669557e-11
[115] 5.088140e-11 9.601433e-11 1.793784e-10 3.317884e-10 6.075883e-10 1.101576e-09
[121] 1.977320e-09 3.513955e-09 6.182621e-09 1.076976e-08 1.857362e-08 3.171349e-08
[127] 5.361035e-08 8.972435e-08 1.486720e-07 2.438961e-07 3.961299e-07 6.369825e-07
[133] 1.014085e-06 1.598374e-06 2.494247e-06 3.853520e-06 5.894307e-06 8.926166e-06
[139] 1.338302e-05 1.986555e-05 2.919469e-05 4.247803e-05 6.119019e-05 8.726827e-05
[145] 1.232219e-04 1.722569e-04 2.384088e-04 3.266819e-04 4.431848e-04 5.952532e-04
[151] 7.915452e-04 1.042093e-03 1.358297e-03 1.752830e-03 2.239453e-03 2.832704e-03
[157] 3.547459e-03 4.398360e-03 5.399097e-03 6.561581e-03 7.895016e-03 9.404908e-03
[163] 1.109208e-02 1.295176e-02 1.497275e-02 1.713686e-02 1.941861e-02 2.178522e-02
[169] 2.419707e-02 2.660852e-02 2.896916e-02 3.122539e-02 3.332246e-02 3.520653e-02
[175] 3.682701e-02 3.813878e-02 3.910427e-02 3.969525e-02 3.989423e-02 3.969525e-02
[181] 3.910427e-02 3.813878e-02 3.682701e-02 3.520653e-02 3.332246e-02 3.122539e-02
[187] 2.896916e-02 2.660852e-02 2.419707e-02 2.178522e-02 1.941861e-02 1.713686e-02
[193] 1.497275e-02 1.295176e-02 1.109208e-02 9.404908e-03 7.895016e-03 6.561581e-03
[199] 5.399097e-03 4.398360e-03 3.547459e-03 2.832704e-03 2.239453e-03 1.752830e-03
[205] 1.358297e-03 1.042093e-03 7.915452e-04 5.952532e-04 4.431848e-04 3.266819e-04
[211] 2.384088e-04 1.722569e-04 1.232219e-04 8.726827e-05 6.119019e-05 4.247803e-05
[217] 2.919469e-05 1.986555e-05 1.338302e-05 8.926166e-06 5.894307e-06 3.853520e-06
[223] 2.494247e-06 1.598374e-06 1.014085e-06 6.369825e-07 3.961299e-07 2.438961e-07
[229] 1.486720e-07 8.972435e-08 5.361035e-08 3.171349e-08 1.857362e-08 1.076976e-08
[235] 6.182621e-09 3.513955e-09 1.977320e-09 1.101576e-09 6.075883e-10 3.317884e-10
[241] 1.793784e-10 9.601433e-11 5.088140e-11 2.669557e-11 1.386680e-11 7.131328e-12
[247] 3.630962e-12 1.830332e-12 9.134720e-13 4.513544e-13 2.207990e-13 1.069384e-13
[253] 5.127754e-14 2.434321e-14 1.144156e-14 5.324148e-15 2.452855e-15 1.118796e-15
[259] 5.052271e-16 2.258809e-16 9.998379e-17 4.381639e-17 1.901082e-17 8.166236e-18
[265] 3.472963e-18 1.462296e-18 6.095758e-19 2.515806e-19 1.027977e-19 4.158599e-20
[271] 1.665588e-20 6.604580e-21 2.592865e-21 1.007794e-21 3.878112e-22 1.477495e-22
[277] 5.573000e-23 2.081177e-23 7.694599e-24 2.816567e-24 1.020731e-24 3.662345e-25
[283] 1.300962e-25 4.575376e-26 1.593111e-26 5.491898e-27 1.874372e-27 6.333538e-28
[289] 2.118819e-28 7.017760e-29 2.301231e-29 7.471002e-30 2.401345e-30 7.641655e-31
[295] 2.407561e-31 7.509729e-32 2.319147e-32 7.090703e-33 2.146384e-33 6.432540e-34
normalProbability <- data.frame(observations = observations,
density = normalProbability)
ggplot(normalProbability,
aes(x = observations,
y = density)) +
geom_bar(stat = "identity",
fill = 'darkred',
color = 'darkred') +
ggtitle("Gaussian Distribution") +
theme_minimal() +
theme(panel.border = element_blank()) +
theme(plot.title = element_text(hjust = .5, size = 10))

observations <- 1:300
normalProbability <- pnorm(observations, mean = 174, sd = 10)
sum(normalProbability)
[1] 126.5
normalProbability <- data.frame(observations = observations,
density = normalProbability)
ggplot(normalProbability,
aes(x = observations,
y = density)) +
geom_bar(stat = "identity",
fill = 'darkred',
color = 'darkred') +
ggtitle("Gaussian Cumulative Distribution") +
ylab("P(height <= x)") +
theme_bw() +
theme(panel.border = element_blank()) +
theme(plot.title = element_text(hjust = .5, size = 10))

Here are some examples of the normal distribution in nature and
society:
Heights of Individuals: The distribution of heights in a
population often follows a normal distribution. The majority of
individuals tend to cluster around the mean height, with fewer
individuals at both extremes (very tall or very short).
IQ Scores: Intelligence quotient (IQ) scores are often
approximated by a normal distribution. In a large population, most
individuals fall around the average IQ score, with fewer individuals
having scores that deviate significantly from the mean.
Body Mass Index (BMI): The distribution of BMI scores, which
relates to body weight and height, tends to approximate a normal
distribution. In a given population, most individuals have BMI values
near the mean, with fewer individuals having extremely high or low BMI
values.
Test Scores: In educational settings, the distribution of test
scores often follows a normal distribution. When a large number of
students take a test, their scores tend to form a bell curve, with most
scores clustering around the average and fewer scores at the
extremes.
Errors in Measurements: Errors in measurement, such as in
scientific experiments or quality control processes, often exhibit a
normal distribution. This phenomenon is known as measurement error or
random error, where the errors tend to center around zero with
decreasing frequency as the magnitude of the error increases.
Natural Phenomena: Many natural phenomena, such as the
distribution of particle velocities in a gas, the sizes of raindrops, or
the distribution of reaction times in human perception, can be modeled
by the normal distribution.
Error Terms in Regression Analysis: In regression analysis, the
assumption of normally distributed error terms is often made. This
assumption allows for efficient statistical inference and hypothesis
testing.
6 The sampling distribution of the sample mean
Let’s assume that some quantity in reality follows a Normal
Distribution with \(\mu\) = 100 and
\(\sigma\) = 12, and draw many, many
random samples of size 100 from this distribution:
n_samples <- 100000
sampleMeans <- sapply(1:n_samples,
function(x) {
mean(rnorm(n = 100, mean = 100, sd = 12))
})
sampleMeans <- data.frame(sampleMean = sampleMeans)
ggplot(sampleMeans,
aes(x = sampleMean)) +
geom_histogram(binwidth = .1,
fill = 'white',
color = 'purple') +
ggtitle("Sampling Distribution of the Sample Mean") +
xlab("Sample Mean") +
theme_bw() +
theme(panel.border = element_blank()) +
theme(plot.title = element_text(hjust = .5, size = 10))

What is the standard deviation of sampleMeans?
sd(sampleMeans$sampleMean)
[1] 1.201998
And recall that the population standard deviation
was defined to be 12. Now:
12/sqrt(100)
[1] 1.2
And this is roughly the same, right?
The standard deviation of the sampling distribution of a
mean - also known as the standard error - is
equal to the standard deviation of the population
(i.e. the true distribution) divided by \(\sqrt(N)\):
\[\sigma_\widetilde{x} =
\sigma/\sqrt(N)\]
and that means that if we know the sample standard
deviation
Standard deviation (SD) measures the dispersion of a dataset relative
to its mean. Standard error of the mean (SEM) measured how much
discrepancy there is likely to be in a sample’s mean compared to the
population mean. The SEM takes the SD and divides it by the square root
of the sample size. Source: Investopedia,
Standard Error of the Mean vs. Standard Deviation: The
Difference
7 The Central Limit Theorem
In the following set of numerical simulations we want to do the
following:
- define a probability distribution by providing a set of parameters,
say \(\mu\) and \(\sigma^2\) for the Normal, or \(\lambda\) for the Poisson, or
n and p for the Binomial;
- each time we pick a distribution, we take
- a random sample of size
sampleN <- 100,
- compute the mean of the obtained random numbers,
and then
- we repeat that either 10, or 100, or 1,000, or 10,000 times (varies
as:
meanSizes <- c(10, 100, 1000, 10000)).
Let’s see what happens:
sampleN <- 100
meanSizes <- c(10, 100, 1000, 10000)
Poisson with \(\lambda = 1\):
# - Poisson with lambda = 1
lambda = 1
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
poisMeans <- sapply(1:meanSize,
function(x) {
mean(rpois(sampleN, lambda))
}
)
hist(poisMeans, 50, col="red",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

Poisson with \(\lambda = 2\):
# - Poisson with lambda = 2
lambda = 2
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
poisMeans <- sapply(1:meanSize,
function(x) {
mean(rpois(sampleN, lambda))
}
)
hist(poisMeans, 50, col="red",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

Poisson with \(\lambda = 10\):
# - Poisson with lambda = 10
lambda = 10
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
poisMeans <- sapply(1:meanSize,
function(x) {
mean(rpois(sampleN, lambda))
}
)
hist(poisMeans, 50, col="red",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

Binomial with p = .1 and n =
1000:
# - Binomial with p = .1 and n = 1000
p = .1
n = 1000
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
binomMeans <- sapply(1:meanSize,
function(x) {
mean(rbinom(n = sampleN, size = n, prob = p)) }
)
hist(binomMeans, 50, col="darkorange",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

Binomial with p = .5 and n =
1000:
# - Binomial with p = .5 and n = 1000
p = .5
n = 100
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
binomMeans <- sapply(1:meanSize,
function(x) {
mean(rbinom(n = sampleN, size = n, prob = p))
}
)
hist(binomMeans, 50, col="darkorange",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

Normal with \(\mu = 10\) and \(\sigma = 1.5\):
# - Normal with mean = 10 and sd = 1.5
mean = 10
sd = 1.5
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
normalMeans <- sapply(1:meanSize,
function(x) {
mean(rnorm(sampleN, mean = mean, sd = sd))
}
)
hist(normalMeans, 50, col="lightblue",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

Normal with \(\mu = 175\) and \(\sigma = 11.4\):
# - Normal with mean = 175 and sd = 11.4
mean = 175
sd = 11.4
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
normalMeans <- sapply(1:meanSize,
function(x) {
mean(rnorm(sampleN, mean = mean, sd = sd))
}
)
hist(normalMeans, 50, col="lightblue",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

Normal with \(\mu = 100\) and \(\sigma = 25\):
# - Normal with mean = 100 and sd = 25
mean = 100
sd = 25
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (meanSize in meanSizes) {
normalMeans <- sapply(1:meanSize,
function(x) {
mean(rnorm(sampleN, mean = mean, sd = sd))
}
)
hist(normalMeans, 50, col="lightblue",
main = paste0("N samples = ", meanSize),
cex.main = .75)
}

What happens if we start increasing sampleN and keep the
number of samples at some decent number, say
meanSize = 10000?
sampleN <- c(10, 100, 1000, 100000)
meanSize <- c(10000)
Poisson with \(\lambda = 3\):
# - Poisson with lambda = 1
lambda = 3
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (sampleN in sampleN) {
poisMeans <- sapply(1:meanSize,
function(x) {
mean(rpois(sampleN, lambda))
}
)
hist(poisMeans, 50, col="red",
main = paste0("Sample N = ", sampleN),
cex.main = .75)
}

Normal with \(\mu = 10\) and \(\sigma = .75\):
sampleN <- c(10, 100, 1000, 100000)
meanSize <- c(10000)
# - Normal with mean = 10 and sd = .75
mean = 10
sd = .75
# - Set plot parameters
par(mfrow = c(2, 2))
# - Plot!
for (sampleN in sampleN) {
normMeans <- sapply(1:meanSize,
function(x) {
mean(rnorm(sampleN, mean = mean, sd = sd))
}
)
hist(normMeans, 50, col="lightblue",
main = paste0("Sample N = ", sampleN),
cex.main = .75)
}

All the same as the number of samples increase?
Exactly:
Central Limit Theorem. In probability theory, the central limit
theorem (CLT) establishes that, in many situations, when independent
random variables are added, their properly normalized sum tends toward a
normal distribution (informally a bell curve) even if the original
variables themselves are not normally distributed. The theorem is a key
concept in probability theory because it implies that probabilistic and
statistical methods that work for normal distributions can be applicable
to many problems involving other types of distributions. Source: Central Limit
Theorem, English Wikipedia, accessed: 2021/02/17
Does it always work? Strictly: NO. Enter Cauchy
Distribution:
# - Cauchy with location = 0, scale = 1
# - set plot parameters
par(mfrow=c(2,2))
# - plot!
for (meanSize in c(100, 1000, 10000, 100000)) {
cauchySums <- unlist(lapply(seq(1:meanSize), function(x) {
sum(rcauchy(1000, location = 0, scale = 1))
}))
hist(cauchySums, 50, col="orange",
main = paste("N samples = ", meanSize, sep=""),
cex.main = .75)
}

… also known as The Witch of
Agnesi…
Aleksandar Cvetković
Goran S. Milovanović
DataKolektiv, 2023.
contact: goran.milovanovic@datakolektiv.com

License: GPLv3
This Notebook is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version. This Notebook is distributed in the hope that
it will be useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. You should have received a
copy of the GNU General Public License along with this Notebook. If not,
see http://www.gnu.org/licenses/.
LS0tCnRpdGxlOiAiQURWQU5DRUQgQU5BTFlTVCAtIEZvdW5kYXRpb25zIGZvciBBZHZhbmNlZCBEYXRhIEFuYWx5dGljcyBpbiBSIC0gU2Vzc2lvbjAwIgphdXRob3I6Ci0gbmFtZTogR29yYW4gUy4gTWlsb3Zhbm92acSHLCBQaEQKICBhZmZpbGlhdGlvbjogRGF0YUtvbGVrdGl2LCBDaGllZiBTY2llbnRpc3QgJiBPd25lcjsgTGVhZCBEYXRhIFNjaWVudGlzdCBmb3Igc21hcnRvY3RvCmFic3RyYWN0OiBudWxsCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aGVtZTogc3BhY2VsYWIKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfZGVwdGg6IDUKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDUKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogJzUnCi0tLQoKIVtdKF9pbWcvREtfTG9nb18xMDAucG5nKQoKKioqCiMgUHJvYmFiaWxpdHkgQm9vdGNhbXAKCioqQXV0aG9yczoqKgoKKipBbGVrc2FuZGFyIEN2ZXRrb3ZpxIcsIFBoZCwgTWF0aGVtYXRpY3MqKgoKKipHb3JhbiBTLiBNaWxvdmFub3ZpxIcsIFBoZCwgUHN5Y2hvbG9neSoqCgojIyMgV2hhdCBkbyB3ZSB3YW50IHRvIGRvIHRvZGF5PwoKIyMjIyBOYXZpZ2F0aW5nIHRoZSBJbnRyaWNhY2llcyBvZiBQcm9iYWJpbGl0eSBUaGVvcnkKCkluIHRoaXMgc2Vzc2lvbiwgd2UgZW1iYXJrIG9uIGEgcmlnb3JvdXMgZXhwbG9yYXRpb24gb2YgUHJvYmFiaWxpdHkgVGhlb3J5LCB0aGUgYmFja2JvbmUgb2Ygc3RhdGlzdGljYWwgcmVhc29uaW5nIGFuZCBhIGNyaXRpY2FsIHRvb2wgaW4gdGhlIGFyc2VuYWwgb2YgYW55IGRhdGEgYW5hbHlzdC4gUHJvYmFiaWxpdHkgVGhlb3J5IGFsbG93cyB1cyB0byBxdWFudGlmeSB0aGUgdW5jZXJ0YWludHkgaW5oZXJlbnQgaW4gdGhlIHdvcmxkIGFyb3VuZCB1cywgb2ZmZXJpbmcgYSBzeXN0ZW1hdGljIGZyYW1ld29yayBmb3IgbWFraW5nIGluZm9ybWVkIGRlY2lzaW9ucyBiYXNlZCBvbiBpbmNvbXBsZXRlIGluZm9ybWF0aW9uLiBTdGFydGluZyB3aXRoIHRoZSBmdW5kYW1lbnRhbCBjb25jZXB0cyBvZiBwcm9iYWJpbGl0eSwgd2Ugd2lsbCBleHBsb3JlIHRoZSBsb2dpY2FsIHN0cnVjdHVyZSBwcm92aWRlZCBieSBLb2xtb2dvcm92J3MgQXhpb21zLCBzZXR0aW5nIHRoZSBzdGFnZSBmb3IgYSBkZWVwZXIgdW5kZXJzdGFuZGluZyBvZiBib3RoIGRpc2NyZXRlIGFuZCBjb250aW51b3VzIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbnMuCgpVdGlsaXppbmcgUiwgd2Ugd2lsbCBkZWx2ZSBpbnRvIHByYWN0aWNhbCBhcHBsaWNhdGlvbnMgYW5kIHRoZW9yZXRpY2FsIHVuZGVycGlubmluZ3Mgb2YgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9ucywgaWxsdXN0cmF0aW5nIGhvdyB0aGVzZSBjb25jZXB0cyB1bmRlcnBpbiBtdWNoIG9mIHN0YXRpc3RpY2FsIGFuYWx5c2lzLiBXZSB3aWxsIHRoZW4gaW52ZXN0aWdhdGUgdGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc2FtcGxlIG1lYW4sIGEgY29uY2VwdCBvZiBlc3NlbnRpYWwgaW1wb3J0YW5jZSBmb3IgdW5kZXJzdGFuZGluZyBpbmZlcmVuY2UgaW4gc3RhdGlzdGljcy4gRmluYWxseSwgd2UnbGwgZGVteXN0aWZ5IHRoZSBDZW50cmFsIExpbWl0IFRoZW9yZW0sIGEgY29ybmVyc3RvbmUgaW4gc3RhdGlzdGljYWwgdGhlb3J5IHRoYXQgZmFjaWxpdGF0ZXMgdGhlIHVuZGVyc3RhbmRpbmcgb2YgZGlzdHJpYnV0aW9uIHNoYXBlcyBhbmQgc2FtcGxpbmcgYmVoYXZpb3JzLiBUaGlzIHNlc3Npb24gd2lsbCBlcXVpcCB5b3Ugd2l0aCBhIHJvYnVzdCBmb3VuZGF0aW9uIGluIFByb2JhYmlsaXR5IFRoZW9yeSwgZW5oYW5jaW5nIHlvdXIgYW5hbHl0aWNhbCBza2lsbHMgYW5kIHByZXBhcmluZyB5b3UgZm9yIG1vcmUgY29tcGxleCBzdGF0aXN0aWNhbCBjaGFsbGVuZ2VzLgoKKipGZWVkYmFjayoqIHNob3VsZCBiZSBzZW5kIHRvIGBnb3Jhbi5taWxvdmFub3ZpY0BkYXRha29sZWt0aXYuY29tYC4gCgpUaGVzZSBub3RlYm9va3MgYWNjb21wYW55IHRoZSBBRFZBTkNFRCBBTkFMWVNUIC0gRm91bmRhdGlvbnMgZm9yIEFkdmFuY2VkIERhdGEgQW5hbHl0aWNzIGluIFIgW0RhdGFLb2xla3Rpdl0oaHR0cDovL3d3dy5kYXRha29sZWt0aXYuY29tL2FwcF9kaXJlY3QvRGF0YUtvbGVrdGl2U2VydmVyLykgdHJhaW5pbmcuCgoqKioKCiMjIyBXZWxjb21lIHRvIFIhCgohW10oX2ltZy9BZHZBbmFseXRpY3NSMjAyNF9CYW5uZXIuanBlZykKCiMjIDAuIFNldHVwCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIDEuIEV2ZW50IGFuZCBFeHBlcmltZW50YWwgUHJvYmFiaWxpdHkKCldoZW4gYXNrZWQ6ICpXaGF0J3MgdGhlIHByb2JhYmlsaXR5IG9mIGxhbmRpbmcgVGFpbHMgd2hlbiB0b3NzaW5nIGEgKGZhaXIpIGNvaW4/KiBZb3UnZCAocHJvYmFibHkpIGFuc3dlcjogJFxmcmFjezF9ezJ9JC4gT3IgNTAlLiBCdXQgd2hhdCBkb2VzIHRoYXQgbWVhbj8KCkFuZCB3aGVuIGFza2VkOiAqV2hhdCdzIHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nIDYgd2hlbiByb2xsaW5nIGEgc2l4LXNpZGVkIChmYWlyKSBkaWU/KiBUaGUgZXhwZWN0ZWQgYW5zd2VyOiAkXGZyYWN7MX17Nn0kLiAqV2hhdCBhYm91dCBOT1QgZ2V0dGluZyA2PyogSXQnczogJFxmcmFjezV9ezZ9JC4gQW5kICpnZXR0aW5nIGEgbnVtYmVyIGxlc3MgdGhhbiBmaXZlPyogJFxmcmFjezJ9ezN9JD8gQnV0IHdoYXQgZG8gdGhvc2UgbnVtYmVycyBtZWFuPwoKQW5kIGFuZCAtICpUaGUgcHJvYmFiaWxpdHkgb2YgZHJhd2luZyBhbiBBY2UgZnJvbSBhIChmYWlyKSBkZWNrIG9mIGNhcmRzPyoKCi0tLQoKTGV0J3MgZ2V0IGJhY2sgdG8gdGhlIGNvaW4uICRcZnJhY3sxfXsyfSQgc2F5cyB5b3UuIEJ1dCB3aGF0IGRvZXMgaXQgbWVhbj8gTWF5YmUgeW91IG1lYW50IHRoYXQgb3V0IG9mIHR3byB0b3NzaW5ncyB5b3UnbGwgbGFuZCBleGFjdGx5IG9uZSBUYWlscyAoJFRIJCBvciAkSFQkKT8gT2theSwgbGV0J3MgdG9zcyBhIGNvaW4gdHdvIHRpbWVzLiBJIGdvdCAkVFQkLiBNYXliZSBteSBhc3N1bXB0aW9uIHdhcyB3cm9uZz8gT3IgSSd2ZSBqdXN0IGdvdCB1bmx1Y2t5PyBMZXQncyB0b3NzIGl0IGFnYWluLiAkSFQkIHRoaXMgdGltZS4gTWF5YmUgSSdtIGFjdHVhbGx5IHJpZ2h0PyBPciBJIGdvdCBsdWNreS4gQW5vdGhlciB0d28gdG9zc2VzISAkSEgkLiBIbW0uLi4gVW5sdWNreSBhZ2Fpbj8gQnV0IGNhbiBJIHRhbGsgYWJvdXQgbHVjayBpbiBtYXRoZW1hdGljcyBhbmQgaW4gaHlwb3RoZXNpcyB0ZXN0aW5nPwoKV2hhdCBhYm91dCB0aGUgb3RoZXIgYW5zd2VyOiA1MCUuIERvZXMgdGhhdCBtZWFuIHRoYXQgSSdsbCBsYW5kIFRhaWxzIGV4YWN0bHkgaGFsZiB0aGUgdGltZXMgd2hlbiB0b3NzaW5nIGEgY29pbiAkTiQgdGltZXM/IExldCdzIHRvc3MgaXQsIHNheSwgMTAgdGltZXMuIEJ1dCBJIGRvbid0IGFjdHVhbGx5IGhhdmUgYSBjb2luLiBMdWNraWx5IEkgaGF2ZSBSIHdoaWNoIGNhbiBzaW11bGF0ZSB0b3NzaW5nIGEgY29pbi4gCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgLSBpbml0IHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9yOiBzZXQuc2VlZCgpCnNldC5zZWVkKDQyKSAKCiMgLSBhIGNvaW4KY29pbiA8LSBjKCdIJywgJ1QnKQoKIyAtIGEgc2FtcGxlOiBudW1lcmljYWwgc2ltdWxhdGlvbiBvZiAxMCBjb2luIHRvc3Nlcwp0b3NzaW5nID0gc2FtcGxlKGNvaW4sIHNpemU9MTAsIHJlcGxhY2UgPSBUUlVFKQoKIyAtIHJlc3VsdAp0b3NzaW5nCmBgYAoKNSBvZiAxMCBUYWlscy4gRXhhY3RseSA1MCUhIEJ1dCwgd2FpdC4gTGV0J3MgdG9zcyB0aGUgY29pbiBhZ2FpbiEgCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnRvc3NpbmcgPSBzYW1wbGUoY29pbiwgc2l6ZT0xMCwgcmVwbGFjZSA9IFRSVUUpCnRvc3NpbmcKYGBgCgpIbSwgNiBvdXQgb2YgMTAgVGFpbHMuIE9rIG5vdyBpcyB0aGlzIGEgZmFpciBjb2luIG9yIG5vdD8KCkxldCdzIHRvc3MgYSBjb2luIDEwMCB0aW1lcyB0aGVuLiAKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KdG9zc2luZyA9IHNhbXBsZShjb2luLCBzaXplPTEwMCwgcmVwbGFjZSA9IFRSVUUpCnRvc3NpbmcKYGBgCkhvdyBtYW55IFRhaWxzLCB0aGVuPwoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQp0YWJsZSh0b3NzaW5nKQpgYGAKCjU2LzEwMC4gTm90IGJhZC4gQnV0IG5vdCBxdWl0ZSA1MCUuIEx1Y2t5LCBidXQgbm90IGFzIHF1aXRlPyAoQnV0IGNhbiBJIHRhbGsgYWJvdXQgbHVjayBpbiBtYXRoZW1hdGljcyBhbmQgaW4gaHlwb3RoZXNpcyB0ZXN0aW5nPykgV2hhdCBpZiB0aG9zZSA1MCUgaXMgYWN0dWFsbHkgYW4gKmFwcHJveGltYXRpb24qIG9mIHRoZSByZXN1bHQ/IExldCdzIHRvc3MgYSBjb2luIDEwMDAgdGltZXMuICAKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KdG9zc2luZyA9IHNhbXBsZShjb2luLCBzaXplPTEwMDAsIHJlcGxhY2UgPSBUUlVFKQp0YWJsZSh0b3NzaW5nKQpgYGAKCjQ5OS8xMDAwLiBUaGF0J3MgKmFsbW9zdCB0aGVyZSouIEFuZCB0aGF0J3Mgbm90IHRoYXQgcXVpdGUgYmFkICphcHByb3hpbWF0aW9uKi4gQnV0IHN0aWxsLi4uIE5vdywgbGV0J3MgdG9zcyBhIGNvaW4gT05FIE1JTExJT04gVElNRVMuCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnRvc3NpbmcgPSBzYW1wbGUoY29pbiwgc2l6ZT0xMF42LCByZXBsYWNlID0gVFJVRSkKdGFibGUodG9zc2luZykKYGBgCgpBbmQgdGhhdCB3b3VsZCBiZSBgciA0OTk2MDEvMTBeNmAgLSBub3QgYmFkIGFuIGFwcHJveGltYXRpb24uLi4KCldoYXQgaWYgdG9zc2VkIGEgY29pbiBtb3JlIHRoYW4gbWlsbGlvbiB0aW1lcz8KCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KdG9zc2luZyA9IHNhbXBsZShjb2luLCBzaXplPTEwXjcsIHJlcGxhY2UgPSBUUlVFKQp0YWJsZSh0b3NzaW5nKQpgYGAKCk5vdyBpdCdzIGByIDQ5OTk1NDEvMTBeN2AgLSBldmVuIGNsb3NlIHRvIDUwOjUwLiBUaGlzIGlzIGV2ZW4gYmV0dGVyLCB2ZWVlZXJ5IGNsb3NlIHRvIDUwJS4KCkFuZCBpZiB3ZSB0b3NzZWQgYSBjb2luICoqaW5maW5pdGVseSBtYW55IHRpbWVzKio/IFRoZW4gdGhlcmUgaXMgbm8gYXBwcm94aW1hdGlvbi4gV2UnZCBnZXQgNTAlIHNoYXJwLiBNYXRoZW1hdGljYWxseSwgd2UgY2FuIHdyaXRlIHRoaXMKCiQkIFAoVCkgPSBcbGltX3tOX3tccm0gVFJJRVN9XHJpZ2h0YXJyb3dcaW5mdHl9XGZyYWN7bl97XHJtIEhJVFN9fXtOX3tccm0gVFJJRVN9fSA9IDAuNSA9IFxmcmFjezF9ezJ9LiQkCgpXaGF0IGFyZSBhbGwgdGhlc2UgbGV0dGVycz8gTGV0J3MgaW50ZXJwcmV0IHRoaXMgZm9ybXVsYSBvbmUgYnkgb25lLiAKCi0gJFQkIGlzIHRoZSAqZXZlbnQqOiBldmVudCBvZiBsYW5kaW5nIFRhaWxzIHdoZW4gdG9zc2luZyBPTkUgY29pbi4gCgotICRQJCBpcyB0aGUgKnByb2JhYmlsaXR5Kjogd2UgbWF5IGNvbnNpZGVyIGl0IGFzIGEgZnVuY3Rpb24gd2hpY2ggKm1lYXN1cmVzKiBob3cgcHJvYmFibGUgYW4gZXZlbnQgaXMuIFNvLCAkUChUKSQgaXMgcHJvYmFiaWxpdHkgb2YgbGFuZGluZyBUYWlscyB3aGVuIHRvc3Npbmcgb25lIGNvaW4uIAoKLSAkTl97XHJtIFRSSUVTfSQgaXMgdGhlIG51bWJlciBvZiB0cmlhbHMgb2YgdGhlIFNBTUUgZXhwZXJpbWVudC4gSW4gb3VyIGNhc2UsIGl0J3MgdGhlIG51bWJlciBvZiB0b3NzaW5nIGEgY29pbi4gCgotICRuX3tccm0gSElUU30kIGlzIHRoZSBudW1iZXIgb2YgJ2hpdHMnLCBpLmUuIHRoZSBudW1iZXIgb2YgZGVzaXJlZCBvdXRjb21lcyBpbiBvdXIgc2V0IG9mIHRyaWFscy4gSW4gb3VyIGNhc2UsIGl0J3MgdGhlIG51bWJlciBvZiBUYWlscyBsYW5kZWQuIAoKLSAkXGxpbV97Tl97XHJtIFRSSUVTfVxyaWdodGFycm93XGluZnR5fSQgaXMgdGhlICpsaW1pdCo6IGEgdmFsdWUgb2YgdGhlIGZyYWN0aW9uICRcZnJhY3tuX3tccm0gSElUU319e05fe1xybSBUUklFU319JCB3aGljaCB3ZSB3b3VsZCBvYnRhaW4gaWYgd2Ugd291bGQgKnRoZW9yZXRpY2FsbHkqIGJlIGFibGUgdG8gdG9zcyBhIGNvaW4gaW5maW5pdGVseSBtYW55IHRpbWVzIGluIG91ciBmaW5pdGUgbGl2ZXMuIEJ1dCB3ZSBjYW4ndCBkbyB0aGF0LiBXZSBjYW4gb25seSBvYnRhaW4gYW4gKmV4cGVyaW1lbnRhbCogKG9yICpzdGF0aXN0aWNhbCopIGFwcHJveGltYXRpb24gb2YgdGhlICp0aGVvcmV0aWNhbCogdmFsdWUgaW4gZmluaXRlIG51bWJlciBvZiB0cmlhbHMuIEFuZCBhcyB3ZSBkZW1vbnN0cmF0ZWQgLSB0aGUgbGFyZ2VyIG51bWJlciBvZiB0cmlhbHMsIHRoZSBiZXR0ZXIgdGhlIGFwcHJveGltYXRpb24uIAoKLS0tCgpMZXQncyBub3cgaWxsdXN0cmF0ZSB0aGlzICpsaW1pdCogb24gYW5vdGhlciBleGFtcGxlOiByb2xsaW5nIGEgc2l4LXNpZGVkIGRpZS4gSW50dWl0aXZlbHkgd2Ugc2F5IHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgYSA2IGlzICRcZnJhY3sxfXs2fSQsIG9yIDE2LjY2Ni4uLiUgLiBTaW1pbGFybHkgYXMgd2l0aCB0aGUgY29pbiwgd2UnbGwgc2ltdWxhdGUgcm9sbGluZyBhIGRpZSBmb3IgdmFyaW91cyBudW1iZXJzIG9mIHRyaWFscyBhbmQgbGlzdCB0aGUgcmVzdWx0cywgaS5lLiBhcHByb3hpbWF0aW9ucyBvZiB0aGVvcmV0aWNhbCBwcm9iYWJpbGl0eS4KCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyAtIG51bWJlcl9vZl9yb2xsczoKbnVtYmVyX29mX3JvbGxzIDwtIHJvdW5kKDEwXnNlcSgxLjc1LCA3LCBieSA9IDAuMDUyNSkpCnByaW50KG51bWJlcl9vZl9yb2xscykKCiMgLSBkaWUgPC0gMTo2CmRpZSA8LSAxOjYKCiMgLSBleHBlcmltZW50CmV4cF9wcm9iIDwtIGxhcHBseShudW1iZXJfb2Zfcm9sbHMsIGZ1bmN0aW9uKHgpIHsKICAKICByb2xscyA8LSBzYW1wbGUoZGllLCB4LCByZXBsYWNlID0gVFJVRSkKICByZXN1bHQgPC0gdGFibGUocm9sbHMpCiAgbnVtYmVyX29mX3NpeGVzID0gcmVzdWx0WzZdCiAgcF9vZl9zaXggPC0gbnVtYmVyX29mX3NpeGVzL3gKICByZXR1cm4oCiAgICBkYXRhLmZyYW1lKE51bWJlck9mUm9sbHMgPSB4LAogICAgICAgICAgICAgICBOdW1iZXJPZlNpeGVzID0gbnVtYmVyX29mX3NpeGVzLAogICAgICAgICAgICAgICBQX1NpeCA9IHBfb2Zfc2l4KQogICAgKQp9KQoKIyAtIHB1dCB0aGUgcmVzdWx0IHRvZ2V0aGVyCmV4cF9wcm9iX2RmIDwtIFJlZHVjZShyYmluZCwgZXhwX3Byb2IpCgojIC0gU2hvdyBtZToKcHJpbnQoZXhwX3Byb2JfZGYpCmBgYApMZXQncyBwbG90IHRoZSByZXN1bHRzCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmdncGxvdChleHBfcHJvYl9kZiwgCiAgICAgICBhZXMoeCA9IGxvZyhOdW1iZXJPZlJvbGxzKSwKICAgICAgICAgICB5ID0gUF9TaXgpKSArIAogIGdlb21fbGluZShjb2xvciA9ICJkYXJrYmx1ZSIsIHNpemUgPSAuMjUpICsKICBnZW9tX3BvaW50KGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJkYXJrYmx1ZSIsIHNpemUgPSAxKSArCiAgeGxhYigiTnVtYmVyIG9mIHJvbGxzIikgKyAKICB5bGFiKCJOdW1iZXIgb2YgU2l4ZXMiKSArIAogIGdndGl0bGUoIlN0YXRpc3RpY2FsIEV4cGVyaW1lbnQ6IFxuUm9sbGluZyBhIGZhaXIgZGllIGFuZCBjb3VudGluZyB0aGUgbnVtYmVyIG9mIHNpeGVzIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSwgc2l6ZSA9IDEwKSkKYGBgCgpGcm9tIHRoZSBzY2F0dGVycGxvdCBhYm92ZSB3ZSBzZWUgaG93IHRoZSB2YWx1ZXMgb2YgcmF0aW8gJFxmcmFje25fe1xybSBISVRTfX17Tl97XHJtIFRSSUVTfX0kIGNvbnZlcmdlIHRvd2FyZHMgdGhlIHByZWRpY3RlZCB0aGVvcmV0aWNhbCBwcm9iYWJpbGl0eSBvZiAxNi42NjYuLi4lIGFzICROX3tccm0gVFJJRVN9XHJpZ2h0YXJyb3dcaW5mdHkkLgoKQnV0LCBpcyB0aGVyZSBhIHdheSB0byBjYWxjdWxhdGUgdGhlb3JldGljYWwgcHJvYmFiaWxpdHkgKipleGFjdGx5Kio/CgojIyAyLiAkXHNpZ21hJC1BbGdlYnJhIG9mIEV2ZW50cyBhbmQgVGhlb3JldGljYWwgUHJvYmFiaWxpdHkKCkluIG9yZGVyIHRvIGJlIGFibGUgdG8gc3BlYWsgcHJvcGVybHkgYWJvdXQgdGhlb3JldGljYWwgcHJvYmFiaWxpdHkgKGFuZCBiZSBhYmxlIHRvIGNvbXB1dGUgaXQpLCB3ZSBuZWVkIHRvIGludHJvZHVjZSAqJFxzaWdtYSQtYWxnZWJyYSBvZiBldmVudHMqIGFuZCAqcHJvYmFiaWxpdHktYXMtYS1tZWFzdXJlKi4gV2UgY2FuIGNvbnNpZGVyICRcc2lnbWEkLWFsZ2VicmEgb2YgZXZlbnRzIGFzIGEgZmFtaWx5IG9mIHNldHMgd2hlcmUgZXZlcnkgc2V0IGlzIGFuIGV2ZW50LCBhbmQgZXZlcnkgZWxlbWVudCBvZiB0aGlzIGV2ZW50LXNldCBpcyBhbiAqb3V0Y29tZSogZm9yIHdoaWNoIGNvbnNpZGVyIHRoZSBldmVuZXQgcmVhbGl6ZWQsIGkuZS4gYSAqZmF2b3JhYmxlIG91dGNvbWUqLiAKCkZvciBleGFtcGxlLCBpZiB3ZSB0b3NzIGEgY29pbiB0d28gdGltZXMsIHRoZW4gb25lIGV2ZW50LXNldCBtaWdodCBiZQoKJCQgQSAtIHtccm0gTGFuZGVkXCAxXCBIZWFkc1wgYW5kXCAxXCBUYWlsc30sICQkCgphbmQgaXRzIGVsZW1lbnRzIGFyZQoKJCQgQSA9IFx7SFQsIFRIXH0uJCQKCkEgc2V0IG9mIGFsbCB0aGUgcG9zc2libGUgb3V0Y29tZXMgZm9yIGEgZ2l2ZW4gZXhwZXJpbWVudC9vYnNlcnZhdGlvbiBpcyBjYWxsZWQgdGhlICp1bml2ZXJzYWwgc2V0Ki4gSXQgaXMgZGVub3RlZCBieSAkXE9tZWdhJCBhbmQgaXQgaXMgdGhlIHNldCB1cG9uIHdoaWNoICRcc2lnbWEkLWFsZ2VicmEgb2YgZXZlbnRzIGlzIGJ1aWx0IHVwb24sIGZyb20gdGhlIHN1YnNldHMgJEFcc3Vic2V0XE9tZWdhJCBvZiB0aGUgdW5pdmVyc2FsIHNldC4gIAoKRm9yIHRvc3NpbmcgYSBjb2luIHR3byB0aW1lcyB3ZSBoYXZlCgokJCBcT21lZ2EgPSBce0hILCBIVCwgVEgsIFRUXH0sJCQKCmFuZCBvYnZpb3VzbHkKCiQkIEEgXHN1YnNldCBcT21lZ2EuICQkCgokJDFeXGNpcmNccXVhZCBcZW1wdHlzZXQsXCBcT21lZ2EgXGluIFxTaWdtYSQkCgokJDJeXGNpcmNccXVhZCBBXkMgXGluIFxTaWdtYSQkCgokJDNeXGNpcmNccXVhZCBBXGN1cCBCIFxpbiBcU2lnbWEkJAoKJCQ0XlxjaXJjXHF1YWQgQVxjYXAgQiBcaW4gXFNpZ21hLiQkCgpXaGF0IGRvIHRoZXNlIGNyeXB0aWMgbWVzc2FnZXMgZXZlbiBtZWFuPyBMZXQncyBleHBsYWluIHRoZW0gb25lIGJ5IG9uZSwgSSBwcm9taXNlIHRoZXkgbWFrZSBzZW5zZS4KCiQkMV5cY2lyY1xxdWFkIFxlbXB0eXNldCxcIFxPbWVnYSBcaW4gXFNpZ21hJCQKClRoaXMgbWVhbnMgdGhhdCB0aGUgZW1wdHkgc2V0ICRcZW1wdHlzZXQkIGFuZCB0aGUgdG90YWwgc2V0ICRcT21lZ2EkIGFyZSBhbHNvIGNvbnNpZGVyZWQgYXMgZXZlbnRzLgoKJFxlbXB0eXNldCQgaXMgY2FsbGVkIGFuICppbXBvc3NpYmxlIGV2ZW50KiwgYW5kIGl0IGRvZXMgbm90IGNvbnRhaW4gYW55IChwb3NzaWJsZSkgb3V0Y29tZS4gCgokXE9tZWdhJCwgdmlld2VkIGFzIGFuIGV2ZW50IGlzIGNhbGxlZCAqY2VydGFpbiBldmVudCogLSBnZXR0aW5nIGFueSBvdXRjb21lIGZyb20gYWxsIHRoZSBwb3NzaWJsZSBvdXRjb21lcyBpcyBkZWZpbml0ZWx5IGEgY2VydGFpbiBldmVudC4gCgotLS0KCiQkMl5cY2lyY1xxdWFkIEFeQyBcaW4gXFNpZ21hJCQKCkFuIGNvbXBsZW1lbnRhcnkgc2V0IG9mICRBJDogJEFeQyA9IFxPbWVnYVxzZXRtaW51cyBBJCBpcyBhbHNvIGNvbnNpZGVyZWQgYXMgYW4gZXZlbnQuIEV2ZXJ5IHVuZmF2b3VyYWJsZSBvdXRjb21lIG9mICRBJCBpcyBmYXZvdXJhYmxlIGZvciAkQV5DJCAoYW5kIHZpY2UgdmVyc2EpLiBTbywgZm9yIHRoZSBldmVudCAkQSQgYXMgZGVmaW5lZCBhYm92ZSwgd2UgaGF2ZToKCiQkIEFeQyA9IFx7SEgsIFRUXH0uICQkCgotLS0KCiQkM15cY2lyY1xxdWFkIEFcY3VwIEIgXGluIFxTaWdtYSQkCgpBIHVuaW9uIG9mIHR3byBldmVudHMgKGRlbm90ZWQgYWxzbyBhcyAkQSArIEIkKSBpcyBhbHNvIGNvbnNpZGVyZWQgYXMgYW4gZXZlbnQuIEJ1dCB3aGF0IGlzIGEgdW5pb24gb2YgdHdvIGV2ZW50cyBhY3R1YWxseT8gV2Ugc2F5IHRoYXQgdGhlIGV2ZW50ICRBXGN1cCBCJCBpcyByZWFsaXplZCB3aGVuIGF0IGxlYXN0IG9uZSBvZiB0aGUgZXZlbnRzICRBJCAqKk9SKiogJEIkIGlzIHJlYWxpemVkLiAkQVxjdXAgQiQgY29udGFpbnMgYWxsIHRoZSBvdXRjb21lcyB3aGljaCBhcmUgZmF2b3VyYWJsZSBmb3IgZWl0aGVyIHRoZSBldmVudCAkQSQgKipPUioqIGV2ZW50ICRCJC4KCkZvciBleGFtcGxlLCBpZiB3ZSBkZWZpbmUgZXZlbnQgJEIkIGFzCgokJEIgLSB7XHJtIExhbmRlZFwgMlwgVGFpbHMsfSQkCgp3ZSBoYXZlIAoKJCQgQVxjdXAgQiA9IFx7SFQsIFRILCBUVFx9LiQkCgotLS0KCiQkNF5cY2lyY1xxdWFkIEFcY2FwIEIgXGluIFxTaWdtYS4kJAoKQW4gaW50ZXJzZWN0aW9uIG9mIHR3byBldmVudHMgKGRlbm90ZWQgYWxzbyBhcyBBQikgaXMgYWxzbyBjb25zaWRlcmVkIGFzIGFuIGV2ZW50LiBXZSBzYXkgdGhhdCB0aGUgZXZlbnQgJEFCJCBpcyByZWFsaXplZCB3aGVuIGJvdGggZXZlbnRzICRBJCAqKkFORCoqICRCJCBhcmUgc2ltdWx0YW5lb3VzbHkgcmVhbGl6ZWQuICRBQiQgY29udGFpbnMgYWxsIHRoZSBvdXRjb21lcyB3aGljaCBhcmUgZmF2b3VyYWJsZSBmb3IgYm90aCBldmVudHMgJEEkICoqQU5EKiogJEIkLiAKCkZvciBleGFtbGUsIGlmIHdlIGRlZmluZSBldmVudCAkQyQgYXMgCgokJCBDIC0ge1xybSBUYWlsc1wgaW5cIHRoZVwgZmlyc3RcIGNvaW5cIHRvc3N9LCAkJAoKd2UgaGF2ZSAKCiQkIEFDID0gXHtIVCwgVEhcfSBcY2FwIFx7VEgsIFRUXH0gPSBce1RIXH0uJCQKClR3byBldmVudHMgJEEkIGFuZCAkQiQgYXJlICptdXR1YWxseSBleGNsdXNpdmUqIG9yICpkaXNqb2ludCogaWYgJEFcY2FwIEIgPSBcZW1wdHlzZXQkLCBpLmUuIGlmIHJlYWxpemF0aW9uIG9mIGJvdGggZXZlbnRzIHNpbXVsdGFuZW91c2x5IGlzIGFuIGltcG9zc2libGUgZXZlbnQuCgpUd28gZXZlbnRzICRBJCBhbmQgJEIkIGFyZSAqaW5kZXBlbmRhbnQqIGlmIHRoZSBvdXRjb21lIG9mIG9uZSBldmVudCBkb2VzIG5vdCBpbmZsdWVuY2UgdGhlIG91dGNvbWUgb2YgdGhlIG90aGVyLiBGb3IgZXhhbXBsZSwgd2hlbiB0b3NzaW5nIGEgY29pbiB0d28gdGltZXMsIHRoZSByZXN1bHQgb2YgdGhlIGZpcnN0IHRvc3MgZG9lcyBub3QgaW5mbHVlbmNlIHRoZSBvdXRjb21lIG9mIHRoZSBzZWNvbmQuCgotLS0KCk9uZSBtb3JlIGltcG9ydGFuIG5vdGlvbiBpcyB0aGUgKmVsZW1lbnRhcnkgZXZlbnQqIC0gYW4gZXZlbnQgY29udGFpbmluZyBhIHNpbmdsZSBwb3NzaWJsZSBvdXRjb21lLiBTbywgZm9yIHRvc3Npbmcgb3VyIGNvaW4gdHdvIHRpbWVzLCBlbGVtZW50YXJ5IGV2ZW50cyBhcmU6ICRce0hIXH0sXCBce0hUXH0sXCBce1RIXH0kIGFuZCAkXHtUVFx9JC4KCiRcc2lnbWEkLWFsZ2VicmFzIG9mIGV2ZW50cyBzZXJ2ZSB1cyBhcyBhIGJyaWdlIGJldHdlZW4gdGhlIG5hdHVyYWwgbGFuZ3VhZ2UgYnkgd2hpY2ggd2UgZGVzY3JpYmUgZXZlbnRzIGFuZCB0aGVpciBvdXRjb21lcyB3aXRoIGZvcm1hbCBtYXRoZW1hdGljYWwgbGFuZ3VhZ2UgdG8gZGVzY3JpYmUgdGhlbSB2aWEgc2V0cywgdGhlaXIgZWxlbWVudHMgYW5kIHNldCBvcGVyYXRpb25zLiBUaGUgdXBzaWRlIG9mIG1hdGhlbWF0aWNhbCBvYmplY3RzIGlzIHRoYXQgaXQgaXMgbmF0dXJhbCB0byBpbXBvc2Ugc29tZSBtZWFzdXJlIG9uIHRoZW0sIGFuZCBub3cgd2UgY2FuIG1lYXN1cmUgdGhlIGV2ZW50cywgaS5lLiBtZWFzdXJlIHRoZSBwcm9iYWJpbGl0eSBvZiBldmVudCByZWFsaXphdGlvbi4gU28gd2UgY2FuIGRlZmluZSBwcm9iYWJpbGl0eS1hcy1hLW1lYXN1cmUsIHRoYXQgaXMgYSBtYXBwaW5nICRQJCBmcm9tICRcc2lnbWEkLWFsZ2VicmEgb2YgZXZlbnRzIHRvIGludGVydmFsIGEgc2V0IG9mIHJlYWwgbnVtYmVycyB2aWEgZm9sbG93aW5nIHNldCBvZiAqYXhpb21zKiAoa25vd24gYXMgKktvbG1vZ29yb3YgQXhpb21zKik6CgoqKkF4aW9tIDEqKjogRm9yIGFueSBldmVudCAkQSQgd2UgaGF2ZQoKJCQgUChBKVxnZXFzbGFudCAwLiQkCgoqKkF4aW9tIDIqKjogRm9yIGNlcnRhaW4gZXZlbnQgJFxPbWVnYSQgd2UgaGF2ZQoKJCQgUChcT21lZ2EpID0gMS4kJAoKKipBeGlvbSAzKio6IEZvciBtdXR1YWxseSBleGNsdXNpdmUgZXZlbnRzICRBXzEsIEFfMiwgXGxkb3RzLCBBX24sIFxsZG90cyQgd2UgaGF2ZQoKJCRQXEJpZyhcYmlnY3VwX3tpPTF9XntcaW5mdHl9QV9pXEJpZykgPSBcc3VtX3tpPTF9XntcaW5mdHl9UChBX2kpLiQkCgpXaGF0IGRvIHRob3NlIGF4aW9tcyB0ZWxsIHVzPwoKKipBeGlvbSAxKiogbWVhbnMgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgYW55IGV2ZW50IGhhcyB0byBiZSBzb21lIG5vbi1uZWdhdGl2ZSByZWFsIG51bWJlcjsgaW4gb3RoZXIgd29yZHMgLSB3ZSBjYW5ub3QgaGF2ZSBuZWdhdGl2ZSBwcm9iYWJpbGl0eSAodGhlIHNhbWUgd2F5IHdlIGNhbm5vdCBoYXZlIG5lZ2F0aXZlIGxlbmd0aCwgc3VyZmFjZSBvciB2b2x1bWUgLSB3aGljaCBhcmUgYWxzbyBtZWFzdXJlcyBvZiBzb21lIGtpbmQpCgoqKkF4aW9tIDIqKiBzYXlzIHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIGEgY2VydGFpbiBldmVudCBpcyAxIChvciAxMDAlKS4gQXMgd2Uga25vdyB0aGF0ICRcT21lZ2EkLCB2aWV3ZWQgYXMgYSBzZXQsIGlzIGEgdW5pdmVyc2FsIHNldCwgaS5lLiBzZXQgdGhhdCBjb250YWlucyBhbGwgdGhlIHBvc3NpYmxlIG91dGNvbWVzIC0gdGhpcyBheGlvbSBhbHNvIHRlbGxzIHVzIHRoYXQgdGhlIHByb2JhYmlsaXR5IG9mIG9ic2VydmluZyBhbnkgb3V0Y29tZSBvZiBhbGwgcG9zc2libGUgZGVmaW5lZCBvdXRjb21lcyBpcyBlcXVhbCB0byAxLiAKCklmIHdlIHdhbnQgdG8gY29tcHV0ZSBhIHByb2JhYmlsaXR5IG9mIHNvbWUgdW5pb24gb2YgbXV0dWFsbHkgZXhjbHVzaXZlIGV2ZW50cywgKipBeGlvbSAzKiogdGVsbHMgdXMgdGhhdCB3ZSBjYW4gZG8gdGhhdCBqdXN0IGJ5IHN1bW1pbmcgdGhlIHByb2JhYmlsaXRpZXMgb2YgZXZlcnkgc2luZ2xlIGV2ZW50LiAKCi0tLQoKQW5kIGhlcmUncyBhIG5pY2UgcmVtaW5kZXIgZm9yICRcc2lnbWEkLWFsZ2VicmFzIGFuZCBLb2xtb2dvcm92IEF4aW9tczoKCiFbXShfaW1nL3Byb2IucG5nKQpUaGVzZSBheGlvbXMgaGF2ZSB2ZXJ5IHVzZWZ1bGwgY29uc2VxdWVuY2VzOyBpZiAkQSQgYW5kICRCJCBhcmUgdHdvIGV2ZW50cywgd2UgaGF2ZToKCiQkIDFeXGNpcmNcIFAoXGVtcHR5c2V0KSA9IDAsICQkCgokJCAyXlxjaXJjXCAwIFxsZXFzbGFudCBQKEEpIFxsZXFzbGFudCAxLCAkJAoKJCQgM15cY2lyY1wgUChBXkMpID0gMSAtIFAoQSksICQkCgokJCA0XlxjaXJjXCBBXHN1YnNldGVxIEIgXFJpZ2h0YXJyb3cgUChBKSBcbGVxc2xhbnQgUChCKS4kJAoKJCQgNV5cY2lyY1wgQSxcIEJcIHtccm0gaW5kZXAufSBcUmlnaHRhcnJvdyBQKEFCKSA9IFAoQSlQKEIpLiQkCgpMZXQncyBub3cgZ28gb3ZlciB0aGVzZSBjb25zZXF1ZW5jZXMuIAoKJCQgMV5cY2lyY1wgUChcZW1wdHlzZXQpID0gMCAkJAoKV2Ugc2F3IHRoYXQgaW1wb3NzaWJsZSBldmVudCBpcyByZXByZXNlbnRlZCBieSAkXGVtcHR5c2V0JC4gU28sIHRoaXMgdGVsbHMgdXMgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgYW4gaW1wb3NzaWJsZSBldmVudCBpcyAwLiAKCi0tLQoKJCQgMl5cY2lyY1wgMCBcbGVxc2xhbnQgUChBKSBcbGVxc2xhbnQgMSAkJAoKTm90IG9ubHkgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgYW4gZXZlbnQgaXMgc29tZSBub24tbmVnYXRpdmUgcmVhbCBudW1iZXIgLSBpdCdzIGEgcmVhbCBudW1iZXIgYmVsb25naW5nIHRvIHRoZSBpbnRlcnZhbCBbMCwgMV07IGFuZCB0aGUgZW5kcG9pbnRzIG9mIHRoaXMgaW50ZXJ2YWwgY29ycmVzcG9uZCB0byB0aGUgaW1wb3NzaWJsZSBldmVudCAoJFAoXGVtcHR5c2V0KSA9IDAkKSBhbmQgY2VydGFpbiBldmVudCAoJFAoXE9tZWdhKSA9IDEkKS4gVGhlIGNsb3NlciB0aGUgcHJvYmFiaWxpdHkgaXMgdG8gMSwgdGhlIG1vcmUgcHJvYmFibGUgaXMgdGhlIHJlYWxpemF0aW9uIG9mIGFuIGV2ZW50LiBUaGlzIGFsc28gYWxvd3MgdXMgdG8gc3BlYWsgYWJvdXQgcHJvYmFiaWxpdGllcyBpbiB0ZXJtcyBvZiBwZXJjZW50cy4gCgotLS0KCiQkIDNeXGNpcmNcIFAoQV5DKSA9IDEgLSBQKEEpICQkCgpUaGlzIHRlbGxzIHVzIGhvdyB0byBzaW1wbHkgY2FsY3VsYXRlIHByb2JhYmlsaXR5IG9mIGEgY29tcGxlbWVudGFyeSBldmVudC4gSWYgYSBwcm9iYWJpbGl0eSBvZiBhbiBldmVudCBoYXBwZW5pbmcgaXMgMjIlLCB0aGVuIHRoZSBwcm9iYWJpbGl0eSBvZiBpdCBOT1QgaGFwcGVuaW5nIGlzIDc4JS4KCi0tLQoKJCQ0XlxjaXJjXCBBXHN1YnNldGVxIEIgXFJpZ2h0YXJyb3cgUChBKSBcbGVxc2xhbnQgUChCKSAkJAoKSWYgc2V0ICRBJCBpcyBjb250YWluZWQgaW4gc2V0ICRCJCwgaS5lLiBpZiBhbGwgdGhlIG91dGNvbWVzIGZhdm91cmFibGUgZm9yIGV2ZW50ICRBJCBhcmUgYWxzbyBmYXZvdXJhYmxlIGZvciBldmVudCAkQiQsIHRoZW4gZXZlbnQgJEEkIGhhcyBzbWFsbGVyIChvciBlcXVhbCkgY2hhbmNlcyBmb3IgcmVhbGl6YXRpb24gdGhhbiB0aGUgZXZlbnQgJEIkLiBJbiBvdGhlciB3b3JkcyAtIGV2ZW50cyB3aXRoIHNtYWxsZXIgc2V0IG9mIGZhdm91cmFibGUgb3V0Y29tZXMgYXJlIGxlc3MgcHJvYmFibGUuIAoKLS0tCgokJCA1XlxjaXJjXCBBLFwgQlwge1xybSBpbmRlcC59IFxSaWdodGFycm93IFAoQUIpID0gUChBKVAoQikuJCQKCklmIHR3byBldmVudHMgYXJlIGluZGVwZW5kYW50LCB0aGVuIHRoZSBwcm9iYWJpbGl0eSBvZiB0aGVtIGJvdGggb2NjdXJpbmcgaXMgZXF1YWwgdG8gdGhlIHByb2R1Y3Qgb2YgcHJvYmFiaWxpdGVzIG9mIGVhY2ggZXZlbnQgb2NjdXJpbmcgaW5kZXBlbmRhbnRseS4gSWYgd2UgdG9zcyBhIGNvaW4gdHdvIHRpbWVzLCB3ZSBjYW4gY2FsY3VsYXRlCgokJFAoSFQpID0gUChIKVAoVCkuJCQKCi0tLQoKQWxsIHRoaXMgdGFsayBhYm91dCB0aGUgUHJvYmFiaWxpdHkgVGhlb3J5LCBidXQgd2Ugc3RpbGwgaGF2ZW4ndCBmaWd1cmVkIG91dCBob3cgdG8gY2FsY3VsYXRlIHRoZW9yZXRpY2FsIHByb2JhYmlsaXR5LiBEb24ndCB3b3JyeSwgd2UgYXJlIGFsbW9zdCB0aGVyZSAtIGFuZCB3ZSBoYXZlIGFsbCB0aGUgaW5ncmVkaWVudHMgdG8gd3JpdGUgYSBmb3JtdWxhIHRoYXQgc3RlbXMgcXVpdGUgbmF0dXJhbGx5IGZyb20gdGhlIHRoZW9yZXRpY2FsIGZvdW5kYXRpb25zIGFib3ZlLiAKCkFzIHdlIHNhdywgcHJvYmFiaWx0eSBvZiBhbiBldmVudCBzaG91bGQgYmUgc29tZSBudW1iZXIgYmV0d2VlbiAwIGFuZCAxLCB3aXRoIHByb2JhYmlsaXRpZXMgb2YgaW1wb3NzaWJsZSBhbmQgY2VydGFpbiBldmVudCBhcyBleHRyZW1lIHZhbHVlcy4gQW5kIHRoZSBiaWdnZXIgdGhlIGV2ZW50L3NldCBpcywgdGhlIGJpZ2VyIGl0cyBwcm9iYWJpbGl0eSBzaG91bGQgYmUuIFRoaXMgbGVhZHMgdXMgdG8gZGVmaW5lIHRoZW9yZXRpY2FsIHByb2JhYmlsaXR5IG9mIGFuIGV2ZW50ICgkQSQpIHZpYSB0aGUgZm9sbG93aW5nIHNpbXBsZSBmb3JtdWxhOgoKJCRQKEEpID0gXGZyYWN7fEF8fXt8XE9tZWdhfH0gPSBcZnJhY3t7XHJtIE5vLlwgb2ZcIGFsbFwgdGhlXCBmYXZvdXJhYmxlXCBvdXRjb21lc1wgZm9yfVwgQX17e1xybSBOby5cIG9mXCBhbGxcIHRoZVwgcG9zc2libGVcIG91dGNvbWVzfX0uJCQKCigkfEF8JCBpcyB0aGUgKmNhcmRpbmFsaXR5KiBvZiBhIHNldCwgaS5lLiBhIG51bWJlciBvZiBlbGVtZW50cyB0aGF0IHNldCBoYXMuKQoKT25lIGNhbiBlYXNpbHkgY2hlY2sgdGhhdCBmb3JtdWxhIGZvciB0aGUgcHJvYmFiaWxpdHksIGFzIGdpdmVuIGFib3ZlLCBzYXRpc2ZpZXMgYWxsIHRoZSBLb2xtb2dvcm92IEF4aW9tcyBhbmQsIG9mIGNvdXJzZSwgYWxsIHRoZSBsaXN0ZWQgY29uc2VxdWVuY2VzLiAKCi0tLQoKTm93IHRoYXQgd2UgaGF2ZSB0aGUgJ2Zvcm11bGEgZm9yIHByb2JhYmlsaXR5JyB3ZSBjYW4gZWFzaWx5IGNhbGN1bGF0ZSBwcm9iYWJpbGl0aWVzIG9mIHRoZSBldmVudHMgbGlzdGVkIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhpcyBub3RlYm9vay4gCgotIFByb2JhYmlsaXR5IG9mIGxhbmRpbmcgVGFpbHMgb24gYSBjb2luIHRvc3M6CgokJFAoVCkgPSBcZnJhY3t8XHtUXH18fXt8XHtILCBUXH18fSA9IFxmcmFjezF9ezJ9LiQkCgoKLSBQcm9iYWJpbGl0eSBvZiBsYW5kaW5nIGF0IGxlYXN0IG9uZSBUYWlscyBvbiB0d28gY29pbiB0b3NzZXMgKGV2ZW50ICRBJCk6CgokJFAoQSkgPSBcZnJhY3t8XHtUSCwgSFQsIFRUXH18fXt8XHtISCwgVEgsIEhULCBUVFx9fH0gPSBcZnJhY3szfXs0fS4kJAoKCi0gUHJvYmFiaWxpdHkgb2YgZ2V0dGluZyA2IHdoZW4gcm9sbGluZyBhIHNpeC1zaWRlZCBkaWU6CgokJFAoWCA9IDYpID0gXGZyYWN7fFx7Nlx9fH17fFx7MSwgMiwgMywgNCwgNSwgNlx9fH0gPSBcZnJhY3sxfXs2fS4kJAoKCgotIFByb2JhYmlsaXR5IG9mIGdldHRpbmcgbGVzcyB0aGFuIDUgd2hlbiByb2xsaW5nIGEgc2l4LXNpZGVkIGRpZToKCiQkUChYIDwgNSkgPSBcZnJhY3t8XHsxLCAyLCAzLCA0XH18fXt8XHsxLCAyLCAzLCA0LCA1LCA2XH18fSA9IFxmcmFjezR9ezZ9ID0gXGZyYWN7Mn17M30uJCQKCgoKLSBQcm9iYWJpbGl0eSBvZiBnZXR0aW5nIGFueSBudW1iZXIgZnJvbSAxIHRvIDYgd2hlbiByb2xsaW5nIGEgc2l4LXNpZGVkIGRpZToKCiQkUCgxXGxlcXNsYW50IFhcbGVxc2xhbnQgNikgPSBcZnJhY3t8XHsxLCAyLCAzLCA0LCA1LCA2XH18fXt8XHsxLCAyLCAzLCA0LCA1LCA2XH18fSA9IFxmcmFjezZ9ezZ9ID0gMS4kJAoKCgotIFByb2JhYmlsaXR5IG9mIGdldHRpbmcgYSA3IHdoZW4gcm9sbGluZyBhIHNpeC1zaWRlZCBkaWU6CgokJFAoWCA9IDcpID0gXGZyYWN7fFxlbXB0eXNldHx9e3xcezEsIDIsIDMsIDQsIDUsIDZcfXx9ID0gXGZyYWN7MH17Nn0gPSAwLiQkCgoKLSBQcm9iYWJpbGl0eSBvZiBnZXR0aW5nIDYgb3IgNyB3aGVuIHJvbGxpbmcgYSAyMC1zaWRlZCBkaWU6CgokJFAoXHtYPTZcfVxjdXBce1g9N1x9KSA9IFAoWD02KSArIFAoWD03KSA9IFxmcmFjezF9ezIwfSArIFxmcmFjezF9ezIwfSA9IFxmcmFjezJ9ezIwfSA9IFxmcmFjezF9ezEwfS4kJAoKCgotIFByb2JhYmlsaXR5IG9mIGdldHRpbmcgbGVzcyB0aGFuIDE5IHdoZW4gcm9sbGluZyBhIDIwLXNpZGVkIGRpZToKCiQkUChYIDwgMTkpID0gMSAtIFAoXHtYIDwgMTlcfV5DKSA9IDEgLSBQKFhcZ2Vxc2xhbnQgMTkpID0gIDEgLVxmcmFje3xcezE5LCAyMFx9fH17fFx7MSwgMiwgXGxkb3RzLCAyMFx9fH0gPSAxIC0gXGZyYWN7Mn17MjB9ID0gXGZyYWN7MTh9ezIwfSA9IFxmcmFjezl9ezEwfS4kJAoKCi0gUHJvYmFiaWxpdHkgb2YgZ2V0dGluZyB0d28gc2l4ZXMgaW4gdHdvIGRpY2Ugcm9sbHM6CgokJFAoXHtYXzEgPSA2XH1cY2FwXHtYXzIgPSA2XH0pID0gUChce1hfMSA9IDZcfSlQKFx7WF8yID0gNlx9KSA9IFxmcmFjezF9ezZ9XGNkb3RcZnJhY3sxfXs2fSA9IFxmcmFjezF9ezM2fS4kJAoKCi0gUHJvYmFiaWxpdHkgb2YgZHJhd2luZyBhbiBBY2UgZnJvbSBhIGRlY2sgb2YgY2FyZHM6CgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpjYXQoJ1AoWCA9IEEpID0gfHtcVTAwMDFGMEExLCBcVTAwMDFGMEIxLCBcVTAwMDFGMEMxLCBcVTAwMDFGMEQxfXwvfFdob2xlIERlY2sgb2YgQ2FyZHN8ID0gNC81MiA9IDEvMTMuJykKYGBgCgpFdmVuIHRob3VnaCB3ZSBoYXZlIGEgdG9vbCB0byBjYWxjdWxhdGUgcHJvYmFiaWxpdHkgb2YgYW4gZXZlbnQgZXhhY3RseSwgd2Ugc2hvdWxkbid0IGZvcmdldCBhYm91dCBleHBlcmltZW50YWwgcHJvYmFiaWxpdHkuIEZpcnN0LCBpdCBjYW4gc2VydmUgdXMgdG8gZXhwZXJpbWVudGFsbHkgY2hlY2sgb3VyIHRoZW9yZXRpY2FsIGNhbGN1bGF0aW9uLiBTZWNvbmRseSwgYW5kIG1vcmUgaW1wb3J0YW50OiBzb21ldGltZXMgY2FsY3VsYXRpbmcgdGhlb3JldGljYWwgcHJvYmFiaWxpdHkgaXMgZGlmZmljdWx0LCBvciBldmVuIGltcG9zc2libGU7IHNvLCBwZXJmb3JtaW5nIHRoZSBleHBlcmltZW50cyBhbmQgbm90aW5nIGRvd24gdGhlIHJlc3VsdHMgaXMgYSB3YXkgdG8gb2J0YWluIHRoZSBwcm9iYWJpbGl0eSBvZiBhbiBldmVudC4gCgpXaGlsZSB3ZSd2ZSBiZWVuIHVzaW5nIHRoZSBnaXZlbiBmb3JtdWxhIGZvciBjYWxjdWxhdGluZyB0aGVvcmV0aWNhbCBwcm9iYWJpbGl0aWVzLCB0aGVyZSB3YXMgYWN0dWFsbHkgb25lIGltcG9ydGFudCB0aGluZyB0aGF0IHdlIGhpZCB1bmRlciB0aGUgcnVnIC0gdGhlIGZvcm11bGEgd29ya3Mgb25seSBpbiBjYXNlIHdoZW4gJFxPbWVnYSQgaXMgZmluaXRlIHNldC4gQnV0IHdoYXQgaWYgJFxPbWVnYSQgaXMgaW5maW5pdGU/IERvZXMgYWxsIHRoaXMgbWF0aGVtYXRpY2FsIGNvbnN0cnVjdGlvbiBicmVha3MgZG93bj8gQWN0dWFsbHkgbm8uICRcc2lnbWEkLWFsZ2VicmFzLCBLb2xtb2dvcm92IEF4aW9tcyBhbmQgdGhlaXIgY29uc2VxdWVuY2VzIGFjdHVhbGx5IGdpdmUgdXMgdG9vbHMgdG8gaGFuZGxlIGV2ZW4gaW5maW5pdGUgJFxPbWVnYSRzLCBhbmQgd2UnbGwgc2VlIGhvdyBsYXRlciBvbi4gCgpCdXQgZmlyc3QgbGV0J3MgdGFsayBhYm91dCAqcmFuZG9tIHZhcmlhYmxlcyouCgojIyAzLiBSYW5kb20gdmFyaWFibGU6IGEgRGlzY3JldGUgVHlwZQoKV2hlbiB3ZSB3ZXJlIGNhbGN1bGF0aW5nIHRoZW9yZXRpY2FsIHByb2JhYmlsaXRpZXMgYSBiaXQgYWJvdmUgd2UgaW50cm9kdWNlZCB0aGUgZm9sbG93aW5nIG5vdGF0aW9uICRQKFggPSA2KSQgb3IgJFAoWCBcZ2Vxc2xhbnQgMTkpJC4gV2hhdCBpcyB0aGlzICRYJD8gSXQgaXMgYWN0dWFsbHkgYSByYW5kb20gdmFyaWFibGUgKFJWKSAtIGEgdmFyYWJsZSB3aGljaCBjYW4gdGFrZSBvbmUgb2YgdGhlIHNldmVyYWwgZGlmZmVyZW50IHZhbHVlcywgZWFjaCB3aXRoIGl0cyBvd24gcHJvYmFiaWxpdHkuIAoKVGhlcmUgYXJlIHR3byB0eXBlcyBvZiByYW5kb20gdmFyaWFibGVzOiAqZGlzY3JldGUqIGFuZCAqKGFic29sdXRlbHkpIGNvbnRpbnVvdXMqLiBWYWx1ZXMgb2YgUlYgYXJlIGVsZW1lbnRzLCBpLmUuIG91dGNvbWVzIG9mIHNldCAkXE9tZWdhJC4gSWYgJFxPbWVnYSQgaXMgZmluaXRlIG9yICpjb3VudGFibHkgaW5maW5pdGUqLCB0aGVuIGFzc29jaWF0ZWQgUlYgaXMgZGlzY3JldGUuIElmICRcT21lZ2EkIGlzICp1bmNvdW50YWJseSBpbmZpbml0ZSosIHRoZW4gdGhlIGNvcnJlc3BvbmRpbmcgUlYgaXMgY29udGludW91cy4gCgpXZSdsbCBzcGVhayBhYm91dCBkaXNjcmV0ZSBSVnMgbm93LiAKCi0tLQoKQSBkaXNjcmV0ZSByYW5kb20gdmFyaWFibGUgaXMgZnVsbHkgZGVzY3JpYmVkIGlmIHdlIGtub3cgYWxsIHRoZSB2YWx1ZXMgaXQgY2FuIHRha2UsIGFuZCBhbGwgdGhlIHByb2JhYmlsaXRpZXMgY29ycmVzcG9uZGluZyB0byB0aG9zZSB2YWx1ZXMuIFRoaXMgJ2Rlc2NyaXB0aW9uJyBvZiBhIHJhbmRvbSB2YXJpYWJsZSBpcyBjYWxsZWQgaXRzICpkaXN0cmlidXRpb24qLiBTbywgJ2tub3dpbmcnIGEgcmFuZG9tIHZhcmlhYmxlIGlzIGVxdWl2YWxlbnQgdG8gJ2tub3dpbmcnIGl0cyBkaXN0cmlidXRpb24uIAoKRm9yIHRvc3NpbmcgYSBjb2luLCBhIHJhbmRvbSB2YXJpYWJsZSAkWCQgY2FuIHRha2UgdHdvIHZhbHVlczogSGVhZHMgYW5kIFRhaWxzLCBlYWNoIHdpdGggcHJvYmFiaWxpdHkgJFxmcmFjezF9ezJ9JC4gV2UgY2FuIHdyaXRlIGl0cyBkaXN0cmlidXRpb24gYXMKCiQkIFggOgpcYmVnaW57cG1hdHJpeH0KSCAmIFRcXApcZnJhY3sxfXsyfSAmIFxmcmFjezF9ezJ9ClxlbmR7cG1hdHJpeH0uJCQKClRoaXMgJ2Z1bmN0aW9uJyB3aGljaCBhc3NpZ25zIGEgcHJvYmFiaWxpdHkgdG8gZWFjaCBvdXRjb21lIGlzIGFsc28gY2FsbGVkICpwcm9iYWJpbGl0eSBtYXNzIGZ1bmN0aW9uKiBvciAqcC5tLmYqLgoKRm9yIHJvbGxpbmcgYSBzaXggc2lkZWQgZGllLCB3ZSBoYXZlIGEgcmFuZG9tIHZhcmlhYmxlIHdoaWNoIGNhbiB0YWtlIHNvbWUgb2YgdGhlIHZhbHVlcyBmcm9tIHRoZSBkaWUgZ2l2ZW4gd2l0aCBwLm0uZgoKJCQgWCA6ClxiZWdpbntwbWF0cml4fQoxICYgMiAmIDMgJiA0ICYgNSAmIDZcXApcZnJhY3sxfXs2fSAmIFxmcmFjezF9ezZ9ICYgXGZyYWN7MX17Nn0gJiBcZnJhY3sxfXs2fSAmIFxmcmFjezF9ezZ9ICYgXGZyYWN7MX17Nn0KXGVuZHtwbWF0cml4fS4kJAoKV2UgY2FuIGV2ZW4gZGVmaW5lIFJWcyBvbiBvdXIgb3duLiBUaGUgb25seSBpbXBvcnRhbnQgdGhpbmcgaXMgdGhhdCBhbGwgdGhlIHByb2JhYmlsaXRlcyBpbiBpdHMgZGlzdHJpYnV0aW9uIG5lZWQgdG8gc3VtIHRvIDEgKGFzIHRoZSB1bmlvbiBvZiBhbGwgZWxlbWVudGFyeSBldmVudHMsIGkuZS4gb2YgZXZlcnkgcG9zc2libGUgb3V0Y29tZSBnaXZlcyAkXE9tZWdhJCwgYW5kICRQKFxPbWVnYSkgPSAxJCkuIAoKRm9yIGV4YW1wbGU6CgoKJCQgWCA6ClxiZWdpbntwbWF0cml4fQotMSAmIDAgJiAyICYgNC43XFwKIDAuMiAmIDAuMDUgJiAwLjQ1ICYgMC4zClxlbmR7cG1hdHJpeH0uJCQKCldlIGNhbiBzaW11bGF0ZSB0aGUgb3V0Y29tZXMgb2YgdGhpcyBSViB1c2luZyBgc2FtcGxlKClgOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpjaG9pY2VzIDwtIGMoLTEsIDAsIDIsIDQuNikKc2FtcGxlKGNob2ljZXMsIHNpemUgPSAxLCBwcm9iID0gYyguMiwgLjA1LCAuNDUsIC4zKSkKYGBgCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmNob2ljZXMgPC0gYygtMSwgMCwgMiwgNC42KQpyZXN1bHQgPC0gc2FtcGxlKGNob2ljZXMsIHNpemUgPSAxMDAsIHByb2IgPSBjKC4yLCAuMDUsIC40NSwgLjMpLCByZXBsYWNlID0gVFJVRSkKcmVzdWx0CmBgYApgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnJlc3VsdF9mcmFtZSA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHJlc3VsdCkpCnJlc3VsdF9mcmFtZQpgYGAKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KZ2dwbG90KHJlc3VsdF9mcmFtZSwKICAgICAgIGFlcyh4ID0gcmVzdWx0LAogICAgICAgICAgIHkgPSBGcmVxLCAKICAgICAgICAgICBjb2xvciA9IHJlc3VsdCwKICAgICAgICAgICBmaWxsID0gcmVzdWx0KSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyAKICB4bGFiKCJWYWx1ZXMiKSArIHlsYWIoIkZyZXF1ZW5jeSIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZ3RpdGxlKCJTdGF0aXN0aWNhbCBFeHBlcmltZW50XG5Tb21lIGRpc2NyZXRlIGRpc3RyaWJ1dGlvbiB0aGF0IEkgaGF2ZSBtYWRlIHVwIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSwgc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgotLS0KCkxldCdzIGFzc3VtZSB3ZSBoYXZlIGEgZm9sbG93aW5nIGRpc2NyZXRlIHJhbmRvbSB2YXJpYWJsZSB3aGljaCBjYW4gdGFrZSBpbmZpbml0ZWx5IG1hbnkgdmFsdWVzOgoKCiQkIFggOgpcYmVnaW57cG1hdHJpeH0KMSAmIDIgJiBcY2RvdHMgJiBrICYgXGNkb3RzIFxcCnBfMSAmIHBfMiAmIFxjZG90cyAmIHBfayAmIFxjZG90cyAmIApcZW5ke3BtYXRyaXh9LiQkCgpVc2luZyBpdHMgZGlzdHJpYnV0aW9uLCB3ZSBjYW4gY2FsY3VsYXRlIHByb2JhYmlsaXRpZXMgb2Ygc29tZSBldmVudHMgaW4gdGhlIGZvbGxvd2luZyBtYW5uZXIgKHdoaWNoIGFsc28gYXBwbGllcyBmb3IgZmluaXRlIFJWcyk6CgokJCBQKFggPSBrKSA9IHBfayAkJAoKJCQgUChYIFxsZXFzbGFudCBrKSA9IHBfMSArIHBfMiArIFxjZG90cyArIHBfayAkJAoKJCQgUChYIDwgaykgPSBQKFggXGxlcXNsYW50IGsgLSAxKSA9IHBfMSArIHBfMiArIFxjZG90cyArIHBfe2stMX0gJCQKCiQkIFAoMyBcbGVxc2xhbnQgWCBcbGVxc2xhbnQgaykgPSBwXzMgKyBwXzQgKyBcY2RvdHMgKyBwX2sgJCQKCiQkIFAoWCA+IGspID0gMSAtIFAoWCBcbGVxc2xhbnQgaykgPSAxIC0gKHBfMSArIHBfMiArIFxjZG90cyArIHBfaykgJCQKCi0tLQoKUHJvYmFiaWxpdHkgJFAoWFxsZXFzbGFudCBrKSQgaXMgcXVpdGUgYW4gaW1wb3J0YW50IG9uZSAtIGl0IGV2ZW4gaGFzIGl0J3MgbmFtZSAqY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24qIG9yICpjLmQuZi4qLiBJdCdzIGRlbm90ZWQgYnkgJEYoaykkLCBhbmQgaXQgc2ltcGx5IHN1bXMgYWxsIHRoZSBwcm9iYWJpbGl0aWVzIGZvciBhbGwgdmFsdWVzIHVwIHRvICRrJDoKCiQkIEYgOgpcYmVnaW57cG1hdHJpeH0KMSAmIDIgJiAgMyAmIFxjZG90cyAmIE5cXApwXzEgJiBwXzEgKyBwXzIgICYgcF8xICsgcF8yICsgcF8zICYgXGNkb3RzICYgMSAKXGVuZHtwbWF0cml4fS4kJAoKIyMgNC4gRGlzY3JldGUgVHlwZSBEaXN0cmlidXRpb25zCgpSYW5kb20gb3IgKnN0b2NoYXN0aWMqIHByb2Nlc3NlcyBhcmUgbm90IGp1c3Qgc29tZSBtYXRoZW1hdGljYWwgaW50ZWxsZWN0dWFsIHBsYXl0aGluZy4gVGhleSBvY2N1ciBhbGwgYXJvdW5kIHVzIGFuZCB3aXRoaW4gdXMuIE1hbnkgc3RvY2hhc3RpYyBuYXR1cmFsIGFuZCBzb2NpYWwgcGhlbm9tZW5hIGJlaGF2ZSBhY2NvcmRpbmcgdG8gc29tZSBkaXN0cmlidXRpb24sIGFuZCBjYW4gYmUgbW9kZWxsZWQgYWNjb3JkaW5nIHRvIHRoYXQgZGlzdHJpYnV0aW9uLiBIZXJlIHdlIGdvIHRocm91Z2ggc29tZSBvZiB0aG9zZSBpbXBvcnRhbnQgZGlzY3JldGUtdHlwZSBkaXN0cmlidXRpb25zLgoKIyMjIEJlcm5vdWxsaSBEaXN0cmlidXRpb24KClRoZSBzaW1wbGVzdCBkaXNjcmV0ZSBkaXN0cmlidXRpb24gaXMgQmVybm91bGxpIERpc3RyaWJ1dGlvbiAtIGl0J3MgYSBkaXN0cmlidXRpb24gb2YgYSBSViB0aGF0IGhhcyBiaW5hcnkgb3V0Y29tZXMgLSB5ZXMvbm8sIGhpdC9taXNzIG9yIHN1Y2Nlc3MvZmFpbHVyZS4gVGhlICpwYXJhbWV0ZXIqIG9mIHRoaXMgZGlzdHJpYnV0aW9uIGlzICRwJCAtIHRoZSBwcm9iYWJpbGl0eSBvZiAnaGl0Jy4gVGhpcyBkaXN0cmlidXRpb24gbG9va3MgbGlrZToKCiQkIFggOgpcYmVnaW57cG1hdHJpeH0KMCAmIDFcXAoxIC0gcCAmIHAKXGVuZHtwbWF0cml4fS4kJAoKVG8gc2FtcGxlIGZyb20gQmVybm91bGxpIERpc3RyaWJ1dGlvbiB3ZSBjYW4gdXNlIGBzYW1wbGUoKWAgd2l0aCB0d28gb3V0Y29tZXMuIFNheSwgd2UgaGF2ZSBhIHBvcHVsYXRpb24gdGhhdCBnaXZlcyBhICd5ZXMnIGFuc3dlciB0byBzb21lIHF1ZXN0aW9uIGluIDIvMyBjYXNlcyBhbmQgJ25vJyBpbiAxLzMgY2FzZXMsIGFuZCB3ZSB3YW50IHRvIGRyYXcgYSBzYW1wbGUgb2YgMTAwIGZyb20gaXQuIEhlcmUncyBhIHNhbXBsZToKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KWCA9IGMoJ3llcycsICdubycpCm5vX3RyaWVzID0gMTAwCm91dGNvbWVzID0gc2FtcGxlKFgsIHByb2IgPSBjKDIvMywgMS8zKSwgc2l6ZSA9IG5vX3RyaWVzLCByZXBsYWNlID0gVFJVRSkKbm9fb3V0Y29tZXMgPSBhcy5kYXRhLmZyYW1lKHRhYmxlKG91dGNvbWVzKSkKZ2dwbG90KG5vX291dGNvbWVzLAogICAgICAgYWVzKHggPSBvdXRjb21lcywKICAgICAgICAgICB5ID0gRnJlcSwgCiAgICAgICAgICAgY29sb3IgPSBvdXRjb21lcywKICAgICAgICAgICBmaWxsID0gb3V0Y29tZXMpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIAogIHhsYWIoIk91dGNvbWVzIikgKyB5bGFiKCJGcmVxdWVuY3kiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgZ2d0aXRsZSgiQmVybm91bGxpJ3MgU3RhdGlzdGljYWwgRXhwZXJpbWVudCIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUsIHNpemUgPSAxMCkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKIyMjIEJpbm9taWFsIERpc3RyaWJ1dGlvbgoKQmlub21pYWwgZGlzdHJpYnV0aW9uIGdpdmVzIGFuIGFuc3dlciB0byB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uOiAqd2hhdCdzIHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nICRrJCBoaXRzIGluICRuJCB0cmlhbHMgb2YgdGhlIHNhbWUgaGl0L21pc3MgZXhwZXJpbWVudCwgaGF2aW5nIHByb2JhYmlsaXR5IG9mIGhpdCBlcXVhbCB0byAkcCQ/KgoKQmlub21pYWwgRGlzdHJpYnV0aW9uIGhhcyB0d28gcGFyYW1ldGVyczoKCi0gJG4kOiBudW1iZXIgb2YgcmVwZXRpdGlvbnMgb2YgdGhlIHNhbWUgZXhwZXJpbWVudAotICRwJDogcHJvYmFiaWxpdHkgb2YgYSBoaXQgKHN1Y2Nlc3MpCgpJZiBhIHJhbmRvbSB2YXJpYWJsZSAkWCQgaGFzIGEgQmlub21pYWwgRGlzdHJpYnV0aW9uIHdpdGggcGFyYW1ldGVycyAkbiQgYW5kICRwJCwgd2Ugd3JpdGUgdGhhdAoKJCQgWCBcc2ltIFxtYXRoY2Fse0J9KG4sIHApLiQkCgpGb3IgZXhhbXBsZSwgd2UnZCBsaWtlIHRvIGtub3cgd2hhdCdzIHRoZSBwcm9iYWJpbGl0eSBvZiBsYW5kaW5nIDcgSGVhZHMgaW4gdG9zc2luZyBhIGNvaW4gMTAgdGltZXMuIFdlIGNhbiBhcHByb3hpbWF0ZSB0aGlzIHByb2JhYmlsaXR5IGJ5IHNhbXBsaW5nIGZyb20gdGhlIEJpbm9taWFsIERpc3RyaWJ1dGlvbiB3aXRoIHBhcmFtZXRlcnMgJG49MTAkIGFuZCAkcD0wLjUkICgkXG1hdGhjYWx7Qn0oMTAwLCAwLjUpJCksIHVzaW5nIGByYmlub20oKWAuCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm91dGNvbWVzID0gcmJpbm9tKG4gPSAxMDAsIHByb2IgPSAuNSwgc2l6ZSA9IDEwKQpvdXRjb21lcwpgYGAKCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm5vX291dGNvbWVzID0gYXMuZGF0YS5mcmFtZSh0YWJsZShvdXRjb21lcykpCmdncGxvdChub19vdXRjb21lcywKICAgICAgIGFlcyh4ID0gb3V0Y29tZXMsCiAgICAgICAgICAgeSA9IEZyZXEpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImRhcmtibHVlIiwgZmlsbCA9ICJ3aGl0ZSIpICsgCiAgeGxhYigiT3V0Y29tZXMiKSArIHlsYWIoIkZyZXF1ZW5jeSIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZ3RpdGxlKCJCaW5vbWlhbCBTdGF0aXN0aWNhbCBFeHBlcmltZW50IikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSwgc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpXZSBjYW4gYWN0dWFsbHkgY2FsY3VsYXRlIHRoZW9yZXRpY2FsIEJpbm9taWFsIHByb2JhYmlsaXRpZXMsIHZpYQoKJCRQKFggPSBrKSA9IFxiaW5vbXtufXtrfXBeaygxLXApXntuLWt9LCQkCgp3aGVyZSAkXGJpbm9te259e2t9JCwgY2FsbGVkICpiaW5vbWlhbCBjb2VmZmljaWVudCosIGlzCgokJFxiaW5vbXtufXtrfSA9IFxmcmFje24hfXtrIShuLWspIX0uJCQKClNheSB3ZSBoYXZlIHNvbWUgdW5mYWlyIGNvaW4gd2hpY2ggbGFuZHMgVGFpbHMgd2l0aCBwcm9iYWJpbGl0eSAwLjI1LCBhbmQgd2UgdG9zcyBpdCB0aHJlZSB0aW1lcy4gVXNpbmcgdGhlIGZvcm11bGEgYWJvdmUgZm9yICRcbWF0aGNhbHtCfSgzLCAwLjI1KSQgd2Ugb2J0YWluIHRoZSBwcm9iYWJpbGl0aWVzIGZvciBudW1iZXIgb2YgVGFpbHMgaW4gMyB0b3NzZXMgJFgkOgoKJCQgWCA6ClxiZWdpbntwbWF0cml4fQowICYgMSAmIDIgJiAzXFwKMC40MjE4NzUgJiAwLjQyMTg3NSAmIDAuMTQwNjI1ICYgMC4wMTU2MjUKXGVuZHtwbWF0cml4fS4kJAoKIyMjIyBJbnRlcmx1ZGU6IEZ1bmN0aW9ucyB0byB3b3JrIHdpdGggcHJvYmFiaWxpdHkgaW4gUgoKQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBleHBlcmltZW50OiBhIHBlcnNvbiByb2xscyBhIGZhaXIgZGljZSB0ZW4gdGltZXMuIAoqKlEuKiogV2hhdCBpcyB0aGUgcHJvYmFiaWxpdHkgb2Ygb2J0YWluaW5nIGZpdmUgb3IgbGVzcyBzaXhlcyBhdCByYW5kb20/CgpXZSBrbm93IHRoYXQgUuKAmXMgYGRiaW5vbSgpYCByZXByZXNlbnRzIHRoZSBiaW5vbWlhbCBwcm9iYWJpbGl0eSBtYXNzIGZ1bmN0aW9uIChwLm0uZi4pLiAKTGV04oCZcyBzZWU6IHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nICpleGFjdGx5KiBmaXZlIHNpeGVzIGF0IHJhbmRvbSBpczoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcEZpdmVTaXhlcyA8LSBkYmlub20oNSwgc2l6ZSA9IDEwLCBwID0gMS82KQpwRml2ZVNpeGVzCmBgYAoKRG8gbm90IGJlIGNvbmZ1c2VkIGJ5IG91ciBhdHRlbXB0IHRvIG1vZGVsIGRpY2Ugcm9sbHMgYnkgYSBiaW5vbWlhbCBkaXN0cmlidXRpb246IGluIGZhY3QsIHRoZXJlIGFyZSBvbmx5IHR3byBvdXRjb21lcyBoZXJlLCAiNiBpcyBvYnRhaW5lZCIgd2l0aCAkcD0xLzYkIGFuZCAiZXZlcnl0aGluZyBlbHNlIiB3aXRoICQx4oiScD01LzYkIQoKVGhlbiwgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgZml2ZSBvciBsZXNzIHRoYW4gZml2ZSBzaXhlcyBmcm9tIHRlbiBzdGF0aXN0aWNhbCBleHBlcmltZW50cyBpczoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcEZpdmVBbmRMZXNzU2l4ZXMgPC0gc3VtKAogIGRiaW5vbSgwLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSgxLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSgyLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSgzLCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSg0LCBzaXplID0gMTAsIHAgPSAxLzYpLAogIGRiaW5vbSg1LCBzaXplID0gMTAsIHAgPSAxLzYpCikKcEZpdmVBbmRMZXNzU2l4ZXMKYGBgCgppbiBvcmRlciB0byByZW1pbmQgb3Vyc2VsdmVzIHRoYXQgdGhlIHByb2JhYmlsaXRpZXMgb2YgYWxsIG91dGNvbWVzIGZyb20gYSBkaXNjcmV0ZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gLSBpbiBvdXIgY2FzZSwgdGhhdCDigJwwIHNpeGVz4oCdLCDigJwxIHNpeOKAnSwg4oCcMiBzaXhlc+KAnSwg4oCcMyBzaXhlc+KAnSwg4oCcNCBzaXhlc+KAnSwgb3Ig4oCcNSBzaXhlc+KAnSBldGMuIG9idGFpbiAtIHdpbGwgZXZlbnR1YWxseSBzdW0gdXAgdG8gb25lLiBIb3dldmVyLCBsZXTigJlzIHdyYXAgdGhpcyB1cCBlbGVnYW50bHkgYnkgdXNpbmcgc2FwcGx5KCkKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcEZpdmVBbmRMZXNzU2l4ZXMgPC0gc3VtKHNhcHBseShzZXEoMCw1KSwgZnVuY3Rpb24oeCkgewogIGRiaW5vbSh4LCBzaXplID0gMTAsIHAgPSAxLzYpCn0pKQpwRml2ZUFuZExlc3NTaXhlcwpgYGAKCm9yLCBldmVuIGJldHRlciwgYnkgcmVjYWxsaW5nIHRoYXQgd2UgYXJlIHdvcmtpbmcgd2l0aCBhIHZlY3Rvcml6ZWQgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2U6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnBGaXZlQW5kTGVzc1NpeGVzIDwtIHN1bShkYmlub20oc2VxKDAsNSksIHNpemUgPSAxMCwgcCA9MS82KSkKcEZpdmVBbmRMZXNzU2l4ZXMKYGBgCgpPZiBjb3Vyc2UsIHdlIGNvdWxkIGhhdmUgdXNlZCB0aGUgKipjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbioqKiAoYy5kLmYpIHRvIGZpZ3VyZSBvdXQgdGhpcyBhcyB3ZWxsOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpwRml2ZUFuZExlc3NTaXhlcyA8LSBwYmlub20oNSwgc2l6ZSA9IDEwLCBwID0gMS82KQpwRml2ZUFuZExlc3NTaXhlcwpgYGAKCiMjIyMgUmFuZG9tIE51bWJlciBHZW5lcmF0aW9uIGZyb20gdGhlIEJpbm9taWFsCgpgcmJpbm9tKClgIHdpbGwgcHJvdmlkZSBhIHZlY3RvciBvZiByYW5kb20gZGV2aWF0ZXMgZnJvbSB0aGUgQmlub21pYWwgZGlzdHJpYnV0aW9uIHdpdGggdGhlIGRlc2lyZWQgcGFyYW1ldGVyLCBlLmcuOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIEdlbmVyYXRlIGEgc2FtcGxlIG9mIHJhbmRvbSBiaW5vbWlhbCB2YXJpYXRlczoKcmFuZG9tQmlub21pYWxzIDwtIHJiaW5vbShuID0gMTAwLCBzaXplID0gMSwgcCA9IC41KQpyYW5kb21CaW5vbWlhbHMKYGBgCgpOb3csIGlmIGVhY2ggZXhwZXJpbWVudCBlbmNvbXBhc3NlcyAxMDAgY29pbiB0b3NzZXM6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnJhbmRvbUJpbm9taWFscyA8LSByYmlub20obiA9IDEwMCwgc2l6ZSA9IDEwMCwgcCA9IC41KQpyYW5kb21CaW5vbWlhbHMgIyBzZWUgdGhlIGRpZmZlcmVuY2U/CmBgYAoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpyYW5kb21CaW5vbWlhbHMgPC0gcmJpbm9tKG4gPSAxMDAsIHNpemUgPSAxMDAwMCwgcCA9IC41KQpyYW5kb21CaW5vbWlhbHMKYGBgCgpMZXTigJlzIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcHJldmlvdXMgZXhwZXJpbWVudDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcmFuZG9tQmlub21pYWxzUGxvdCA8LSBkYXRhLmZyYW1lKHN1Y2Nlc3MgPSByYW5kb21CaW5vbWlhbHMpCmdncGxvdChyYW5kb21CaW5vbWlhbHNQbG90LCAKICAgICAgIGFlcyh4ID0gc3VjY2VzcykpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMCwgCiAgICAgICAgICAgICAgICAgZmlsbCA9ICd3aGl0ZScsIAogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2RhcmtibHVlJykgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpJbnRlcnByZXRhdGlvbjogd2Ugd2VyZSBydW5uaW5nIDEwMCBzdGF0aXN0aWNhbCBleHBlcmltZW50cywgZWFjaCB0aW1lIGRyYXdpbmcgYSBzYW1wbGUgb2YgMTAwMDAgb2JzZXJ2YXRpb25zIG9mIGEgZmFpciBjb2luICgkcD0uNSQpLiBBbmQgbm93LAoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpyYW5kb21CaW5vbWlhbHMgPC0gcmJpbm9tKDEwMDAwMCwgc2l6ZSA9IDEwMDAwMCwgcCA9IC41KQpyYW5kb21CaW5vbWlhbHNQbG90IDwtIGRhdGEuZnJhbWUoc3VjY2VzcyA9IHJhbmRvbUJpbm9taWFscykKZ2dwbG90KHJhbmRvbUJpbm9taWFsc1Bsb3QsIAogICAgICAgYWVzKHggPSBzdWNjZXNzKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwLCAKICAgICAgICAgICAgICAgICBmaWxsID0gJ3doaXRlJywgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAnZGFya2JsdWUnKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCuKApiB3ZSB3ZXJlIHJ1bm5pbmcgMTAwMDAgc3RhdGlzdGljYWwgZXhwZXJpbWVudHMsIGVhY2ggdGltZSBkcmF3aW5nIGEgc2FtcGxlIG9mIDEwMDAwMCBvYnNlcnZhdGlvbnMgb2YgYSBmYWlyIGNvaW4gKCRwPS41JCkuCgpTbywgd2UgaGF2ZSB0aGUgUHJvYmFiaWxpdHkgTWFzcyBGdW5jdGlvbiAocC5tLmYpOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpoZWFkcyA8LSAwOjEwMApiaW5vbWlhbFByb2JhYmlsaXR5IDwtIGRiaW5vbShoZWFkcywgc2l6ZSA9IDEwMCwgcCA9IC41KQpzdW0oYmlub21pYWxQcm9iYWJpbGl0eSkKYGBgCgpXaGVyZSBgc3VtKGJpbm9taWFsUHJvYmFiaWxpdHkpID09IDFgIGlzIGBUUlVFYCBiZWNhdXNlIHRoaXMgaXMgYSBkaXNjcmV0ZSBkaXN0cmlidXRpb24gc28gaXRzIFByb2JhYmlsaXR5IE1hc3MgRnVuY3Rpb25zIG91dHB1dHMgcHJvYmFiaWxpdHkgaW5kZWVkIQoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpiaW5vbWlhbFByb2JhYmlsaXR5IDwtIGRhdGEuZnJhbWUoaGVhZHMgPSBoZWFkcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbnNpdHkgPSBiaW5vbWlhbFByb2JhYmlsaXR5KQpnZ3Bsb3QoYmlub21pYWxQcm9iYWJpbGl0eSwgCiAgICAgICBhZXMoeCA9IGhlYWRzLCAKICAgICAgICAgICB5ID0gZGVuc2l0eSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIAogICAgICAgICAgIGZpbGwgPSAnd2hpdGUnLAogICAgICAgICAgIGNvbG9yID0gJ2RhcmtibHVlJykgKwogIGdndGl0bGUoIkJpbm9taWFsIFAuTS5GLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUsIHNpemUgPSAxMCkpCmBgYAoKVGhlIEN1bXVsYXRpdmUgRGlzdHJpYnV0aW9uIEZ1bmN0aW9uIChjLmQuZiksIG9uIHRoZSBvdGhlciBoYW5kOgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpoZWFkcyA8LSAxOjEwMApiaW5vbWlhbFByb2JhYmlsaXR5IDwtIHBiaW5vbShoZWFkcywgc2l6ZSA9IDEwMCwgcCA9IC41KQpzdW0oYmlub21pYWxQcm9iYWJpbGl0eSkKYGBgCgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpiaW5vbWlhbFByb2JhYmlsaXR5IDwtIGRhdGEuZnJhbWUoaGVhZHMgPSBoZWFkcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN1bXByb2IgPSBiaW5vbWlhbFByb2JhYmlsaXR5KQpnZ3Bsb3QoYmlub21pYWxQcm9iYWJpbGl0eSwgCiAgICAgICBhZXMoeCA9IGhlYWRzLCAKICAgICAgICAgICB5ID0gY3VtcHJvYikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIAogICAgICAgICAgIGZpbGwgPSAnd2hpdGUnLAogICAgICAgICAgIGNvbG9yID0gJ2RhcmtibHVlJykgKwogIHlsYWIoIlAoaGVhZHMgPD0geCkiKSArIAogIGdndGl0bGUoIkJpbm9taWFsIEMuRC5GLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUsIHNpemUgPSAxMCkpCmBgYAoKIyMjIyBRdWFudGlsZSBGdW5jdGlvbiBvZiB0aGUgQmlub21pYWwgZGlzdHJpYnV0aW9uCgpUaGUgcXVhbnRpbGUgaXMgZGVmaW5lZCBhcyB0aGUgc21hbGxlc3QgdmFsdWUgJHgkIHN1Y2ggdGhhdCAkRih4KVxnZXEgcCQsIHdoZXJlICRGJCBpcyB0aGUgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKGMuZC5mLik6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnFiaW5vbShwID0gLjAxLCBzaXplID0gMTAwLCBwcm9iID0gLjUpCmBgYAoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpxYmlub20ocCA9IC45OSwgc2l6ZSA9IDEwMCwgcHJvYiA9IC41KQpgYGAKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcWJpbm9tKHAgPSAuMDEsIHNpemUgPSAyMDAsIHByb2IgPSAuNSkKYGBgCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnFiaW5vbShwID0gLjk5LCBzaXplID0gMjAwLCBwcm9iID0gLjUpCmBgYAoKU2ltaWxhcmx5LCB3ZSBjb3VsZCBoYXZlIG9idGFpbmVkICRRXzEkLCAkUV8zJCwgYW5kIHRoZSBtZWRpYW4sIGZvciBzYXkgbiBvZiAxMDAgKHRoYXQgaXMgYHNpemVgIGluIGBxYmlub20oKWApIGFuZCAkcCQgb2YgJC41JCAodGhhdCBpcyBgcHJvYmAgaW4gYHFiaW5vbSgpYCk6CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnFiaW5vbShwID0gLjI1LCBzaXplID0gMTAwLCBwcm9iID0gLjUpCmBgYAoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpxYmlub20ocCA9IC41LCBzaXplID0gMTAwLCBwcm9iID0gLjUpCmBgYAoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpxYmlub20ocCA9IC43NSwgc2l6ZSA9IDEwMCwgcHJvYiA9IC41KQpgYGAKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcWJpbm9tKHAgPSAuMjUsIHNpemUgPSAxMDAwLCBwcm9iID0gLjUpCmBgYAoKSGVyZSBhcmUgc29tZSBleGFtcGxlcyBvZiB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uIGluIG5hdHVyZSBhbmQgc29jaWV0eToKCi0gQ29pbiBGbGlwczogV2hlbiBmbGlwcGluZyBhIGZhaXIgY29pbiBtdWx0aXBsZSB0aW1lcywgdGhlIG51bWJlciBvZiBoZWFkcyBvYnRhaW5lZCBmb2xsb3dzIGEgYmlub21pYWwgZGlzdHJpYnV0aW9uLiBFYWNoIGZsaXAgaXMgYW4gaW5kZXBlbmRlbnQgdHJpYWwgd2l0aCB0d28gb3V0Y29tZXMgKGhlYWRzIG9yIHRhaWxzKSBhbmQgYW4gZXF1YWwgcHJvYmFiaWxpdHkgb2Ygc3VjY2VzcyAoMC41IGZvciBhIGZhaXIgY29pbikuCgotIFByb2R1Y3QgUXVhbGl0eSBDb250cm9sOiBJbiBtYW51ZmFjdHVyaW5nIHByb2Nlc3NlcywgdGhlIG51bWJlciBvZiBkZWZlY3RpdmUgaXRlbXMgaW4gYSBwcm9kdWN0aW9uIGJhdGNoIGNhbiBvZnRlbiBiZSBtb2RlbGVkIHVzaW5nIGEgYmlub21pYWwgZGlzdHJpYnV0aW9uLiBFYWNoIGl0ZW0gaXMgaW5zcGVjdGVkIGluZGVwZW5kZW50bHksIGFuZCBpdCBpcyBjbGFzc2lmaWVkIGFzIGVpdGhlciBkZWZlY3RpdmUgb3Igbm9uLWRlZmVjdGl2ZSBiYXNlZCBvbiBwcmVkZXRlcm1pbmVkIGNyaXRlcmlhLgoKLSBFbGVjdGlvbiBPdXRjb21lczogSW4gYSB0d28tcGFydHkgcG9saXRpY2FsIHN5c3RlbSwgdGhlIGRpc3RyaWJ1dGlvbiBvZiBzZWF0cyB3b24gYnkgZWFjaCBwYXJ0eSBpbiBhIHNlcmllcyBvZiBlbGVjdGlvbnMgY2FuIGJlIG1vZGVsZWQgdXNpbmcgYSBiaW5vbWlhbCBkaXN0cmlidXRpb24uIEVhY2ggZWxlY3Rpb24gaXMgdHJlYXRlZCBhcyBhbiBpbmRlcGVuZGVudCB0cmlhbCB3aXRoIHR3byBwb3NzaWJsZSBvdXRjb21lcyAod2luIG9yIGxvc3MpIGFuZCBhIGZpeGVkIHByb2JhYmlsaXR5IG9mIHN1Y2Nlc3MgZm9yIGVhY2ggcGFydHkuCgotIE1lZGljYWwgVHJpYWxzOiBDbGluaWNhbCB0cmlhbHMgb2Z0ZW4gaW52b2x2ZSB0ZXN0aW5nIHRoZSBlZmZlY3RpdmVuZXNzIG9mIGEgdHJlYXRtZW50IG9yIG1lZGljYXRpb24gb24gYSBncm91cCBvZiBwYXRpZW50cy4gVGhlIGJpbm9taWFsIGRpc3RyaWJ1dGlvbiBjYW4gYmUgdXNlZCB0byBtb2RlbCB0aGUgbnVtYmVyIG9mIHBhdGllbnRzIHdobyByZXNwb25kIHBvc2l0aXZlbHkgdG8gdGhlIHRyZWF0bWVudCwgd2hlcmUgZWFjaCBwYXRpZW50IGlzIGNvbnNpZGVyZWQgYSB0cmlhbCB3aXRoIGEgZml4ZWQgcHJvYmFiaWxpdHkgb2YgcmVzcG9uc2UuCgotIFNwb3J0cyBQZXJmb3JtYW5jZTogVGhlIHN1Y2Nlc3MgcmF0ZSBvZiBhbiBhdGhsZXRlIGluIGEgc3BlY2lmaWMgdHlwZSBvZiBhY3Rpb24sIHN1Y2ggYXMgZnJlZSB0aHJvd3MgaW4gYmFza2V0YmFsbCBvciBwZW5hbHR5IGtpY2tzIGluIHNvY2NlciwgY2FuIGJlIG1vZGVsZWQgdXNpbmcgYSBiaW5vbWlhbCBkaXN0cmlidXRpb24uIEVhY2ggYXR0ZW1wdCByZXByZXNlbnRzIGFuIGluZGVwZW5kZW50IHRyaWFsIHdpdGggdHdvIG91dGNvbWVzIChzdWNjZXNzIG9yIGZhaWx1cmUpIGFuZCBhIGZpeGVkIHByb2JhYmlsaXR5IG9mIHN1Y2Nlc3MgZm9yIHRoZSBhdGhsZXRlLgoKLSBHZW5ldGljIEluaGVyaXRhbmNlOiBUaGUgdHJhbnNtaXNzaW9uIG9mIGNlcnRhaW4gZ2VuZXRpYyB0cmFpdHMgZnJvbSBwYXJlbnRzIHRvIG9mZnNwcmluZyBjYW4gYmUgbW9kZWxlZCB1c2luZyBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbi4gRm9yIGV4YW1wbGUsIHRoZSBwcm9iYWJpbGl0eSBvZiBhIGNoaWxkIGluaGVyaXRpbmcgYSBzcGVjaWZpYyBhbGxlbGUgZnJvbSBhIGhldGVyb3p5Z291cyBwYXJlbnQgZm9sbG93cyBhIGJpbm9taWFsIGRpc3RyaWJ1dGlvbi4KCi0gU3VydmV5IFJlc3BvbnNlczogSW4gb3BpbmlvbiBwb2xscyBvciBtYXJrZXQgcmVzZWFyY2ggc3VydmV5cywgdGhlIGRpc3RyaWJ1dGlvbiBvZiByZXNwb25zZXMgdG8gYSB5ZXMtb3Itbm8gcXVlc3Rpb24gY2FuIGJlIG1vZGVsZWQgdXNpbmcgYSBiaW5vbWlhbCBkaXN0cmlidXRpb24uIEVhY2ggcGFydGljaXBhbnQgcHJvdmlkZXMgYSBzaW5nbGUgcmVzcG9uc2UsIHdoaWNoIGNhbiBiZSBjYXRlZ29yaXplZCBhcyBlaXRoZXIgYSBzdWNjZXNzICh5ZXMpIG9yIGZhaWx1cmUgKG5vKSBvdXRjb21lLgoKIyMjIFBvaXNzb24gRGlzdHJpYnV0aW9uCgoqUG9pc3NvbiogaXMgYSBkaXNjcmV0ZSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gd2l0aCAqbWVhbiBhbmQgdmFyaWFuY2UgY29ycmVsYXRlZCouIFRoaXMgaXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgbnVtYmVyIG9mIG9jY3VycmVuY2VzIG9mIGluZGVwZW5kZW50IGV2ZW50cyBpbiBhIGdpdmVuIGludGVydmFsLgoKVGhlIHAubS5mLiBpcyBnaXZlbiBieToKCiQke1AoWD1rKX0gPSBcZnJhY3tcbGFtYmRhXntrfWVeey1cbGFtYmRhfX17ayF9JCQKd2hlcmUgJFxsYW1iZGEkIGlzIHRoZSBhdmVyYWdlIG51bWJlciBvZiBldmVudHMgcGVyIGludGVydmFsLCBhbmQgJGsgPSAwLCAxLCAyLC4uLiQKCkZvciB0aGUgUG9pc3NvbiBkaXN0cmlidXRpb24sIHdlIGhhdmUgdGhhdCB0aGUgbWVhbiAodGhlIGV4cGVjdGF0aW9uKSBpcyB0aGUgc2FtZSBhcyB0aGUgdmFyaWFuY2U6CgokJFggXHNpbSBQb2lzc29uKFxsYW1iZGEpIFxSaWdodGFycm93IFxsYW1iZGEgPSBFKFgpID0gVmFyKFgpICQkCgoqRXhhbXBsZS4qIChGb2xsb3dpbmcgYW5kIGFkYXB0aW5nIGZyb20gTGFkaXNsYXVzIEJvcnRraWV3aWN6LCAxODk4KS4gQXNzdW1wdGlvbjogb24gdGhlIGF2ZXJhZ2UsIDEwIHNvbGRpZXJzIGluIHRoZSBQcnVzc2lhbiBhcm15IHdlcmUga2lsbGVkIGFjY2lkZW50YWxseSBieSBob3JzZSBraWNrIG1vbnRobHkuIFdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgMTcgb3IgbW9yZSBzb2xkaWVycyBpbiB0aGUgUHJ1c3NpYW4gYXJteSB3aWxsIGJlIGFjY2lkZW50YWxseSBraWxsZWQgYnkgaG9yc2Uga2lja3MgZHVyaW5nIHRoZSBtb250aD8gTGV0J3Mgc2VlOgoKYGBgIHtyIGVjaG8gPSBUfQp0cmFnZWRpZXMgPC0gcHBvaXMoMTYsIGxhbWJkYT0xMCwgbG93ZXIudGFpbD1GQUxTRSkgICAjIHVwcGVyIHRhaWwgKCEpCnRyYWdlZGllcwpgYGAKClNpbWlsYXJseSBhcyB3ZSBoYXZlIHVzZWQgYHBiaW5vbSgpYCB0byBjb21wdXRlIGN1bXVsYXRpdmUgcHJvYmFiaWxpdHkgZnJvbSB0aGUgYmlub21pYWwgZGlzdHJpYnV0aW9uLCBoZXJlIHdlIGhhdmUgdXNlZCBgcHBvaXMoKWAgZm9yIHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4gVGhlIGBsb3dlci50YWlsPUZgIGFyZ3VtZW50IHR1cm5lZCB0aGUgY3VtdWxhdGl2ZSBpbnRvIGEgZGVjdW11bGF0aXZlIChvciBzdXJ2aXZvcikgZnVuY3Rpb246IGJ5IGNhbGxpbmcgYHBwb2lzKDE3LCBsYW1iZGE9MTAsIGxvd2VyLnRhaWw9RkFMU0UpYCB3ZSBoYXZlIGFza2VkIG5vdCBmb3IgJFAoWCBcbGVxIGspJCwgYnV0IGZvciAkUChYID4gaykkIGluc3RlYWQuIEhvd2V2ZXIsIGlmIHRoaXMgaXMgdGhlIGNhc2UsIHRoYW4gb3VyIGBwcG9pcygxNiwgbGFtYmRhPTEwKWAgYW5zd2VyIHdvdWxkIGJlIGluY29ycmVjdCwgYW5kIHRoYXQgaXMgd2h5IHdlIGNhbGxlZDogYHBwb2lzKDE2LCBsYW1iZGE9MTAsIGxvd2VyLnRhaWw9RkFMU0UpYCBpbnN0ZWFkLiBDYW4geW91IHNlZSBpdD8gWW91IGhhdmUgdG8gYmUgdmVyeSBjYXJlZnVsIGFib3V0IGhvdyBleGFjdGx5IHlvdXIgcHJvYmFiaWxpdHkgZnVuY3Rpb25zIGFyZSBkZWZpbmVkIChjLmYuIGBQb2lzc29uIHtzdGF0c31gIGRvY3VtZW50YXRpb24gYXQgKGh0dHBzOi8vc3RhdC5ldGh6LmNoL1ItbWFudWFsL1ItZGV2ZWwvbGlicmFyeS9zdGF0cy9odG1sL1BvaXNzb24uaHRtbCkgYW5kIGZpbmQgb3V0IHdoZXRoZXIgYGxvd2VyLnRhaWw9VGAgaW1wbGllcyAkUChYID4gaykkIG9yICRQKFggXGdlcSBrKSQpLgoKYGBgIHtyIGVjaG8gPSBUfQojIENvbXBhcmU6CnRyYWdlZGllcyA8LSBwcG9pcygxNywgbGFtYmRhPTEwLCBsb3dlci50YWlsPVRSVUUpICAgIyBsb3dlciB0YWlsICghKQp0cmFnZWRpZXMKYGBgCgpUaGlzIF5eIGlzIHRoZSBhbnN3ZXIgdG8gdGhlIHF1ZXN0aW9uIG9mIHdoYXQgd291bGQgYmUgdGhlIHByb2JhYmlsaXR5IG9mIDE3ICphbmQgYW5kIGxlc3MgdGhhbiAxNyogZGVhdGhzLgoKVGhlIHNhbWUgbG9naWMgdG8gZ2VuZXJhdGUgcmFuZG9tIGRldmlhdGVzIGFzIHdlIGhhdmUgb2JzZXJ2ZWQgaW4gdGhlIEJpbm9taWFsIGNhc2UgaXMgcHJlc2VudCBoZXJlOyB3ZSBoYXZlIGBycG9pcygpYDoKCmBgYCB7ciBlY2hvID0gVH0KcG9pc3NvbkRldmlhdGVzIDwtIHJwb2lzKDEwMDAwMCxsYW1iZGEgPSA1KQpwb2lzc29uRGV2aWF0ZXMgPC0gZGF0YS5mcmFtZShldmVudHMgPSBwb2lzc29uRGV2aWF0ZXMpCmdncGxvdChwb2lzc29uRGV2aWF0ZXMsIAogICAgICAgYWVzKHggPSBldmVudHMpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgCiAgICAgICAgICAgICAgICAgZmlsbCA9ICd3aGl0ZScsIAogICAgICAgICAgICAgICAgIGNvbG9yID0gJ2RhcmtvcmFuZ2UnKSArCiAgZ2d0aXRsZSgiUG9pc3NvbiBEaXN0cmlidXRpb24iKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41LCBzaXplID0gMTApKQpgYGAKCk9ic2VydmUgaG93IHRoZSBzaGFwZSBvZiB0aGUgUG9pc3NvbiBkaXN0cmlidXRpb24gY2hhbmdlcyB3aXRoIGl0cyBtZWFuIGFuZCB2YXJpYW5jZSwgYm90aCByZXByZXNlbnRlZCBhcyAkXGxhbWJkYSQ6CgpgYGAge3IgZWNobyA9IFR9CmxhbWJkYSA8LSAxOjIwCnBvaXNzb25EZXZpYXRlcyA8LSBsYXBwbHkobGFtYmRhLCBycG9pcywgbiA9IDEwMDApCnBvaXNzb25EZXZpYXRlcyA8LSByZWR1Y2UocG9pc3NvbkRldmlhdGVzLCByYmluZCkKZGltKHBvaXNzb25EZXZpYXRlcykKYGBgCgpPaywgdGhlbjoKCmBgYHtyIGVjaG8gPSBUfQpwb2lzc29uRGV2aWF0ZXMgPC0gYXMuZGF0YS5mcmFtZShwb2lzc29uRGV2aWF0ZXMpCnBvaXNzb25EZXZpYXRlcyRpZCA8LSAxOmRpbShwb2lzc29uRGV2aWF0ZXMpWzFdCnBvaXNzb25EZXZpYXRlcyA8LSBwb2lzc29uRGV2aWF0ZXMgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gLWlkKSAlPiUgCiAgc2VsZWN0KC1uYW1lKQpwb2lzc29uRGV2aWF0ZXMkaWQgPC0gZmFjdG9yKHBvaXNzb25EZXZpYXRlcyRpZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gc29ydCh1bmlxdWUocG9pc3NvbkRldmlhdGVzJGlkKSkpCmBgYAoKYW5kIGZpbmFsbHk6CgpgYGB7ciBlY2hvID0gVCwgZmlnLmhlaWdodCA9IDV9CmdncGxvdChwb2lzc29uRGV2aWF0ZXMsIAogICAgICAgYWVzKHggPSB2YWx1ZSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCAKICAgICAgICAgICAgICAgICBmaWxsID0gJ3doaXRlJywgCiAgICAgICAgICAgICAgICAgY29sb3IgPSAnZGFya29yYW5nZScpICsKICB4bGFiKCJFdmVudHMiKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbihsYW1iZGEpKSArIAogIGZhY2V0X3dyYXAofmlkLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHN0cmlwLnRleHQgPSAgZWxlbWVudF90ZXh0KHNpemUgPSAxMikpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUsIHNpemUgPSAyMCkpCmBgYAoKTGV0J3Mgc3R1ZHkgb25lIGltcG9ydGFudCBwcm9wZXJ0eSBvZiB0aGUgUG9pc3NvbiBkaXN0cmlidXRpb246CgpgYGAge3IgZWNobyA9IFR9CmxhbWJkYSA8LSAxOjEwMApwb2lzc29uTWVhbiA8LSBzYXBwbHkobGFtYmRhLCBmdW5jdGlvbih4KSB7CiAgbWVhbihycG9pcygxMDAwMDAseCkpCn0pCnBvaXNzb25WYXIgPC0gc2FwcGx5KGxhbWJkYSwgZnVuY3Rpb24oeCkgewogIHZhcihycG9pcygxMDAwMDAseCkpCn0pCnBvaXNzb25Qcm9wZXJ0eSA8LSBkYXRhLmZyYW1lKG1lYW4gPSBwb2lzc29uTWVhbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhbmNlID0gcG9pc3NvblZhcikKZ2dwbG90KHBvaXNzb25Qcm9wZXJ0eSwgCiAgICAgICBhZXMoeCA9IG1lYW4sIAogICAgICAgICAgIHkgPSB2YXJpYW5jZSkpICsgCiAgZ2VvbV9saW5lKGNvbG9yID0gImRhcmtvcmFuZ2UiLCBzaXplID0gLjI1KSArIAogIGdlb21fcG9pbnQoY29sb3IgPSAiZGFya29yYW5nZSIsIGZpbGwgPSAiZGFya29yYW5nZSIsIHNpemUgPSAxLjUpICsgCiAgeGxhYigiRShYKSIpICsgeWxhYigiVmFyKFgpIikgKyAKICBnZ3RpdGxlKCJQb2lzc29uIERpc3RyaWJ1dGlvblxuVGhlIE1lYW4gaXMgZXF1YWwgdG8gVmFyaWFuY2UiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41LCBzaXplID0gMTApKQpgYGAKCkhlcmUgYXJlIHNvbWUgZXhhbXBsZXMgb2YgaG93IHRoZSBQb2lzc29uIGRpc3RyaWJ1dGlvbiBjYW4gYmUgb2JzZXJ2ZWQgaW4gdmFyaW91cyBjb250ZXh0czoKCi0gTmF0dXJhbCBEaXNhc3RlcnM6IFRoZSBvY2N1cnJlbmNlIG9mIGVhcnRocXVha2VzLCB2b2xjYW5pYyBlcnVwdGlvbnMsIG9yIG1ldGVvcml0ZSBpbXBhY3RzIGNhbiBiZSBtb2RlbGVkIHVzaW5nIGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIFdoaWxlIHRoZXNlIGV2ZW50cyBhcmUgcmFyZSwgdGhleSB0ZW5kIHRvIGZvbGxvdyBhIHBhdHRlcm4gb2YgcmFuZG9tIG9jY3VycmVuY2Ugb3ZlciB0aW1lLgoKLSBUcmFmZmljIEFjY2lkZW50czogVGhlIGZyZXF1ZW5jeSBvZiB0cmFmZmljIGFjY2lkZW50cyBhdCBhIHBhcnRpY3VsYXIgaW50ZXJzZWN0aW9uIG9yIGFsb25nIGEgc3BlY2lmaWMgcm9hZCBjYW4gb2Z0ZW4gYmUgYXBwcm94aW1hdGVkIGJ5IGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIFRoZSBhc3N1bXB0aW9uIGlzIHRoYXQgYWNjaWRlbnRzIGhhcHBlbiByYW5kb21seSwgYW5kIHRoZSBudW1iZXIgb2YgYWNjaWRlbnRzIGluIGEgZ2l2ZW4gdGltZSBwZXJpb2QgZm9sbG93cyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLgoKLSBQaG9uZSBDYWxscyB0byBhIENhbGwgQ2VudGVyOiBUaGUgbnVtYmVyIG9mIGluY29taW5nIHBob25lIGNhbGxzIHRvIGEgY2FsbCBjZW50ZXIgZHVyaW5nIGEgc3BlY2lmaWMgdGltZSBpbnRlcnZhbCBjYW4gb2Z0ZW4gYmUgbW9kZWxlZCB1c2luZyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLiBFdmVuIHRob3VnaCBjYWxsIHZvbHVtZXMgbWF5IHZhcnksIHRoZXkgdGVuZCB0byBmb2xsb3cgYSBwYXR0ZXJuIG9mIHJhbmRvbSBvY2N1cnJlbmNlIHRocm91Z2hvdXQgdGhlIGRheS4KCi0gUmFkaW9hY3RpdmUgRGVjYXk6IFRoZSBkZWNheSBvZiByYWRpb2FjdGl2ZSBwYXJ0aWNsZXMgZm9sbG93cyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLiBFYWNoIHBhcnRpY2xlIGhhcyBhIGZpeGVkIHByb2JhYmlsaXR5IG9mIGRlY2F5aW5nIHdpdGhpbiBhIGdpdmVuIHRpbWUgcGVyaW9kLCBhbmQgdGhlIG51bWJlciBvZiBkZWNheXMgd2l0aGluIHRoYXQgdGltZSBwZXJpb2QgZm9sbG93cyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLgoKLSBCaXJ0aCBhbmQgRGVhdGggUmF0ZXM6IEluIHBvcHVsYXRpb24gc3R1ZGllcywgdGhlIG51bWJlciBvZiBiaXJ0aHMgb3IgZGVhdGhzIGluIGEgcGFydGljdWxhciByZWdpb24gb3ZlciBhIGZpeGVkIHRpbWUgcGVyaW9kIGNhbiBiZSBhcHByb3hpbWF0ZWQgdXNpbmcgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4gV2hpbGUgYmlydGhzIGFuZCBkZWF0aHMgbWF5IGV4aGliaXQgc2Vhc29uYWwgb3IgbG9uZy10ZXJtIHRyZW5kcywgdGhlIG9jY3VycmVuY2VzIHdpdGhpbiBzaG9ydCBpbnRlcnZhbHMgY2FuIGJlIG1vZGVsZWQgYXMgcmFuZG9tIGV2ZW50cy4KCi0gRW1haWwgQXJyaXZhbDogVGhlIGFycml2YWwgb2YgZW1haWxzIGluIGFuIGluYm94IGNhbiBvZnRlbiBiZSBtb2RlbGVkIHVzaW5nIGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIFRoZSBhc3N1bXB0aW9uIGlzIHRoYXQgZW1haWxzIGFycml2ZSByYW5kb21seSwgYW5kIHRoZSBudW1iZXIgb2YgZW1haWxzIHJlY2VpdmVkIHdpdGhpbiBhIGZpeGVkIHRpbWUgcGVyaW9kIGNhbiBmb2xsb3cgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4KCi0gTW9sZWN1bGFyIENvbGxpc2lvbiBFdmVudHM6IEluIHBoeXNpY3MgYW5kIGNoZW1pc3RyeSwgdGhlIG51bWJlciBvZiBjb2xsaXNpb24gZXZlbnRzIGJldHdlZW4gbW9sZWN1bGVzIHdpdGhpbiBhIGdpdmVuIHZvbHVtZSBjYW4gYmUgZGVzY3JpYmVkIHVzaW5nIGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIFRoaXMgYXBwbGllcyB0byBzY2VuYXJpb3Mgc3VjaCBhcyBnYXMgZGlmZnVzaW9uIG9yIHRoZSBtb3ZlbWVudCBvZiBwYXJ0aWNsZXMgaW4gYSBmbHVpZC4KCi0gTmV0d29yayBUcmFmZmljOiBUaGUgbnVtYmVyIG9mIHBhY2tldHMgYXJyaXZpbmcgYXQgYSBuZXR3b3JrIHJvdXRlciBvciB0aGUgbnVtYmVyIG9mIHJlcXVlc3RzIHJlY2VpdmVkIGJ5IGEgd2ViIHNlcnZlciB3aXRoaW4gYSBzcGVjaWZpYyB0aW1lIGludGVydmFsIGNhbiBvZnRlbiBiZSBtb2RlbGVkIHVzaW5nIGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIFRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBpbiBhbmFseXppbmcgbmV0d29yayBjb25nZXN0aW9uIGFuZCBjYXBhY2l0eSBwbGFubmluZy4KCi0gRGVmZWN0aXZlIFByb2R1Y3RzOiBUaGUgbnVtYmVyIG9mIGRlZmVjdGl2ZSBwcm9kdWN0cyBpbiBhIG1hbnVmYWN0dXJpbmcgcHJvY2VzcyBjYW4gYmUgbW9kZWxlZCB1c2luZyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLiBJdCBoZWxwcyBhc3Nlc3MgdGhlIHF1YWxpdHkgY29udHJvbCBtZWFzdXJlcyBhbmQgZXN0aW1hdGUgdGhlIGxpa2VsaWhvb2Qgb2YgZW5jb3VudGVyaW5nIGRlZmVjdGl2ZSBpdGVtcyBpbiBhIHByb2R1Y3Rpb24gYmF0Y2guCgotIEhvc3BpdGFsIEFkbWlzc2lvbnM6IFRoZSBudW1iZXIgb2YgcGF0aWVudHMgYWRtaXR0ZWQgdG8gYSBob3NwaXRhbCB3aXRoaW4gYSBnaXZlbiB0aW1lIHBlcmlvZCBjYW4gb2Z0ZW4gYmUgYXBwcm94aW1hdGVkIGJ5IGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIFRoaXMgaW5mb3JtYXRpb24gYWlkcyBpbiByZXNvdXJjZSBhbGxvY2F0aW9uIGFuZCBzdGFmZmluZyBkZWNpc2lvbnMuCgotIFJhcmUgRGlzZWFzZSBPY2N1cnJlbmNlOiBUaGUgb2NjdXJyZW5jZSBvZiByYXJlIGRpc2Vhc2VzIHdpdGhpbiBhIHBvcHVsYXRpb24gY2FuIGJlIG1vZGVsZWQgdXNpbmcgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4gVGhpcyBkaXN0cmlidXRpb24gaGVscHMgZXN0aW1hdGUgdGhlIGxpa2VsaWhvb2Qgb2YgcmFyZSBkaXNlYXNlcyBhbmQgZXZhbHVhdGUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgcHJldmVudGl2ZSBtZWFzdXJlcyBvciB0cmVhdG1lbnRzLgoKLSBOYXR1cmFsIEJpcnRoIEludGVydmFsczogVGhlIHRpbWUgaW50ZXJ2YWxzIGJldHdlZW4gYmlydGhzIGluIGFuaW1hbCBwb3B1bGF0aW9ucywgc3VjaCBhcyBhIHBhcnRpY3VsYXIgc3BlY2llcyBvZiBiaXJkcywgY2FuIGZvbGxvdyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLiBUaGlzIGhlbHBzIHJlc2VhcmNoZXJzIHVuZGVyc3RhbmQgcmVwcm9kdWN0aXZlIHBhdHRlcm5zIGFuZCBwb3B1bGF0aW9uIGR5bmFtaWNzLgoKLSBJbnN1cmFuY2UgQ2xhaW1zOiBUaGUgbnVtYmVyIG9mIGluc3VyYW5jZSBjbGFpbXMgZmlsZWQgd2l0aGluIGEgc3BlY2lmaWMgdGltZSBwZXJpb2QsIHN1Y2ggYXMgYXV0b21vYmlsZSBhY2NpZGVudHMgb3IgcHJvcGVydHkgZGFtYWdlIGNsYWltcywgY2FuIG9mdGVuIGJlIG1vZGVsZWQgdXNpbmcgYSBQb2lzc29uIGRpc3RyaWJ1dGlvbi4gVGhpcyBhc3Npc3RzIGluc3VyZXJzIGluIHJpc2sgYXNzZXNzbWVudCBhbmQgcHJpY2luZyBwb2xpY2llcy4KCi0gQXJyaXZhbCBvZiBDdXN0b21lcnM6IFRoZSBhcnJpdmFsIG9mIGN1c3RvbWVycyBhdCBhIHJldGFpbCBzdG9yZSBvciB0aGUgbnVtYmVyIG9mIGN1c3RvbWVycyBpbiBhIHF1ZXVlIGF0IGEgYmFuayBjYW4gYmUgYXBwcm94aW1hdGVkIHVzaW5nIGEgUG9pc3NvbiBkaXN0cmlidXRpb24uIFRoaXMgaW5mb3JtYXRpb24gYWlkcyBpbiBvcHRpbWl6aW5nIHNlcnZpY2UgY2FwYWNpdHkgYW5kIHdhaXQgdGltZSBlc3RpbWF0aW9uLgoKLSBNb2xlY3VsYXIgRXZlbnRzIGluIEdlbmV0aWNzOiBUaGUgb2NjdXJyZW5jZSBvZiBnZW5ldGljIG11dGF0aW9ucyBvciByZWNvbWJpbmF0aW9uIGV2ZW50cyBpbiBhIEROQSBzZXF1ZW5jZSBjYW4gYmUgbW9kZWxlZCB1c2luZyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLiBUaGlzIGhlbHBzIHJlc2VhcmNoZXJzIHN0dWR5IGdlbmV0aWMgdmFyaWF0aW9ucyBhbmQgZXZvbHV0aW9uYXJ5IHByb2Nlc3Nlcy4KCiMjIDUgUmFuZG9tIHZhcmlhYmxlOiBhIENvbnRpbnVvdXMgVHlwZQoKV2Ugc3Bva2UgYWJvdXQgUlZzIHRoYXQgY2FuIHRha2UgdmFsdWVzIGZyb20gdW5pdmVyc2FsIHNldCAkXE9tZWdhJCB3aGljaCBpcyBlaXRoZXIgZmluaXQgb3IgY291bnRhYmx5IGluZmluaXRlOyB0aGVzZSB3ZXJlIGRpc2NyZXRlLXR5cGUgcmFuZG9tIHZhcmlhYmxlcy4gQnV0IHdoYXQgaWYgJFxPbWVnYSQgaXMgdW5jb3VudGFibHkgaW5maW5pdGUsaS5lLiB3aGF0IGlmIHJhbmRvbSB2YXJpYWJsZSAkWCQgY2FuIHRha2UgYW55IHJlYWwgbnVtYmVyIGFzIGEgdmFsdWU/IFRoZW4gd2UgaGF2ZSAqKGFic29sdXRlbHkpIGNvbnRpbnVvdXMqIHJhbmRvbSB2YXJpYWJsZS4KCkFzIHdpdGggZGlzY3JldGUtdHlwZSBSVnMsIGNvbnRpbnVvdXMtdHlwZSBSVnMgaGF2ZSB0aGVpciBkaXN0cmlidXRpb24uIFdpdGggZGlzY3JldGUgUlZzIHdlIHVzZSBwcm9iYWJpbGl0eSBtYXNzIGZ1bmN0aW9uIChwLm0uZikgdG8gZGVzY3JpYmUgdGhlaXIgZGlzdHJpYnV0aW9uLiBXaXRoIGNvbnRpbnVvdXMgUlZzLCB3ZSBoYXZlICpwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIChwLmQuZikqIHdoaWNoICdkZXNjcmliZXMnIHRoZW0uIE9uZSBwLmQuZi4gJFx2YXJwaGkoeCkkIG9mIGEgY29udGludW91cyByYW5kb20gdmFyaWFibGUgbWF5IGxvb2sgbGlrZSB0aGlzOgoKIVtdKF9pbWcvQ29udGludW91c1Byb2JhYmlsaXR5LmpwZWcpCkVhY2ggcC5kLmYgb2YgYSBjb250aW51b3VzIFJWIHNob3VsZCBiZSBkZWZpbmVkLCBjb250aW51b3VzIGFuZCBub24tbmVnYXRpdmUgYWxtb3N0IGV2ZXJ5d2hlcmUgb24gdGhlIHdob2xlIHJlYWwtbnVtYmVyIGxpbmUsIGFuZDoKCiQkXGludF97LVxpbmZ0eX1eeytcaW5mdHl9XHZhcnBoaSh4KWR4ID0gMS4kJAoKRG9uJ3Qgd29ycnkgYWJvdXQgdGhlIGludGVncmFsIC0gd2UgYXJlIG5vdCBnb2luZyB0byBjb21wdXRlIGludGVncmFscyBpbiB0aGlzIGNvdXJzZS4gQnV0IHdlIGRvIG5lZWQgdGhlbSB0byBjYWxjdWxhdGUgcHJvYmFiaWxpdGllcyBmb3IgY29udGludW91cyBSVnMuIFRoZSBpbnRlZ3JhbCBhYm92ZSB0ZWxscyB1cyB0aGF0IHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZSBvZiBwLmQuZi4gaXMgYWx3YXlzIGVxdWFsIHRvIDEuIFRoaXMgc2VlbXMgZmFtaWxpYXI/IFRoaXMgaXMgYWN0dWFsbHkgY29tcGxldGVseSBhbmFsb2dvdXMgdG8gdGhlIGZhY3QgdGhhdCwgZm9yIGRpc2NyZXRlIFJWLCBhbGwgcHJvYmFiaWxpdGllcyBpbiBpdHMgcC5tLmYuIG5lZWQgYWx3YXlzIHRvIHN1bSB0byAxLiAKCgpTbywgd2hhdCdzIHRoZSBwcm9iYWJpbGl0eSBvZiBSViwgaGF2aW5nIHAuZC5mIGFzIGluIHRoZSBmaWd1cmUgYWJvdmUsIHRvIHRha2UgdmFsdWUgLTUuNT8gSXQncwoKJCRQKFggPSAtMC41KSA9IDAuJCQKCk9LLCB0aGF0IGtpbmRhIG1ha2VzIHNlbnNlLiBCdXQsIHdoYXQgYWJvdXQgdGhlIHByb2JhYmlsaXR5IG9mIHRha2luZyB2YWx1ZSAwPwoKJCRQKFggPSAwKSA9IDAuJCQKCldhaXQsIHdoYXQ/IEFuZCB0YWtpbmcgdmFsdWUgJFxzcXJ0ezJ9JD8KCiQkUChYID0gXHNxcnR7Mn0pID0gMC4kJAoKSXMgaXQgYWx3YXlzIGdvaW5nIHRvIGJlIHplcm8/IFllcy4gQmVjYXVzZSB0aGUgcHJvYmFiaWxpdHkgb2YgaGl0dGluZyBhIHBvaW50IGV4YWN0bHkgeW91IHdhbnQsIG91dCBvZiB1bmNvdW50YWJseSBpbmZpbml0ZWx5IG1hbnkgb3RoZXJzIGlzIC0gb2YgY291cnNlLCB6ZXJvLiBCdXQgaG93IHdlIGNhbiBjYWxjdWxhdGUgcHJvYmFiaWxpdGllcyBhdCBhbGwsIGlmIHRoZSBwcm9iYWJpbGl0eSBvZiBldmVyeSBwb2ludCBpcyB6ZXJvPyBXZSBqdXN0IG5lZWQgdG8gYnJvYWRlbiBvdXIgdmlld3MgLSBub3QgdG8gYXNrIGFib3V0IHRoZSBwcm9iYWJpbGl0eSBvZiBoaXR0aW5nIGEgc2luZ2xlIHBvaW50ICR4JCBhbW9uZyByaWRpY3Vsb3VzbHkgaW5maW5pdGVseSBtYW55LCBidXQgdG8gYXNrIGFib3V0IHRoZSBwcm9iYWJpbGl0eSBvZiBoaXR0aW5nIGFueSBwb2ludCBpbiB0aGUgc21hbGwgaW50ZXJ2YWwgJFxEZWx0YSB4JCB3aGljaCBjb250YWlucyAkeCQuLi4gYW5kIHVuY291bnRhYmx5IGluZmluaXRlIG1hbnkgb3RoZXIgcG9pbnRzLiBXaGVuIHdvcmtpbmcgd2l0aCBjb250aW51b3VzIFJWcyB3ZSBjYW4gaG9wZSB0byBnZXQgcG9zaXRpdmUgcHJvYmFiaWxpdHkgb25seSBpZiB3ZSB3b3JrIHdpdGggaW50ZXJ2YWxzLCBubyBtYXR0ZXIgaG93IGJpZyBvciBzbWFsbC4gCgpIb3dldmVyLCB0byBnZXQgcG9zaXRpdmUgcHJvYmFiaWxpdGllcywgdGhvc2UgaW50ZXJ2YWxzIG5lZWQgdG8gYmUgZWl0aGVyIGZ1bGx5IG9yIHBhcnRpYWxseSBvbiB0aGUgKnN1cHBvcnQqIG9mIHAuZC5mLiBTdXBwb3J0IGlzIHRoZSBwYXJ0IG9mIHAuZC5mLiBjdXJ2ZSB3aGVyZSB0aGlzIGZ1bmN0aW9uIGlzIHBvc2l0aXZlLCBpLmUuICRcdmFycGhpKHgpID4gMCQuIEZvciByZWFsbHkgc21hbGwgaW50ZXJ2YWxzICRcRGVsdGEgeCQgdGhhdCBhcmUgb3V0c2lkZSBvZiB0aGUgc3VwcG9ydCwgaS5lLiBmb3Igd2hpY2ggJFx2YXJwaGkoeCkgPSAwJCwgdGhlcmUgaXMgMCBwcm9iYWJpbGl0eSBmb3IgUlYgJFgkIHRvIHRha2UgYW55IHZhbHVlIGZyb20gdGhhdCBpbnRlcnZhbC4gT24gdGhlIG90aGVyIGhhbmQsIGlmIHdlIG9ic2VydmUgYSByZWFsbHkgc21hbGwgaW50ZXJ2YWwgJFxEZWx0YSB4JCBhcm91bmQgdGhlIHBvaW50IHdoZXJlIHAuZC5mLiB0YWtlcyBpdHMgbWF4aW1hbCB2YWx1ZSAtIHRoZXJlIGlzIHRoZSBoaWdoZXN0IHByb2JhYmlsaXR5IG9mIFJWICRYJCB0YWtpbmcgdmFsdWVzIGZyb20gdGhpcyBpbnRlcnZhbC4gCgpJbiBvdGhlciB3b3JkczogaGlnaGVyIHRoZSB2YWx1ZSBvZiAkXHZhcnBoaSh4KSQgZm9yIHNvbWUgJHgkIC0gaGlnaGVyIHRoZSBwcm9iYWJpbGl0eSBvZiBSViAkWCQgZ2V0dGluZyBzb21lIHZhbHVlIGZyb20gYSB2ZXJ5IHNtYWxsIGludGVydmFsICRcRGVsdGEgeCQgYXJvdW5kICR4JC4gKGJ1dCBub3QgaW4gJHgkIGl0c2VsZjsgdGhlcmUsIHRoZSBwcm9iYWJpbGl0eSBpcyB6ZXJvKQoKLS0tIAoKU28sIHdlIGNhbiBjYWxjdWxhdGUgcHJvYmFiaWxpdHkgb2YgY29udGludW91cyBSViBpbiBhbnkgaW50ZXJ2YWwuIEhvdyBkbyB3ZSBjYWxjdWxhdGUgcHJvYmFiaWxpdHkgb2YgUlYgJFgkIHRha2luZyB2YWx1ZXMgaW4gc29tZSBpbnRlcnZhbCAkW2EsIGJdJCB3aGVyZSAkYSQgYW5kICRiJCBhcmUgdHdvIG51bWJlcnM/IFNpbXBseSwgdXNpbmcgdGhlIGZvcm11bGE6CgokJFAoYSBcbGVxc2xhbnQgWCBcbGVxc2xhbnQgYikgPSBcaW50X2FeYlx2YXJwaGkoeClkeC4kJAoKSXQncyB0aGF0IGludGVncmFsIGFnYWluLi4uIEFuZCBhZ2FpbiBpdCBtYWtlcyBzZW5zZSBhZ2Fpbi4gUmVtZW1iZXIgdGhhdCwgdXNpbmcgS29sbW9nb3JvdiBBeGlvbXMsIHdlIGRlZmluZWQgcHJvYmFiaWxpdHkgYXMgYSBtZWFzdXJlPyBXZSBhbHNvIG1lbnRpb25lZCB0aGF0IGludGVncmFsIGlzIHRoZSBtZWFzdXJlIG9mIHRoZSBhcmVhIHVuZGVyIHRoZSBjdXJ2ZS4gU28sIHVzaW5nIGFuIGludGVncmFsIHRvIG1lYXN1cmUgYXJlYSB1bmRlciB0aGUgY3VydmUgb2YgcC5kLmYgb24gc29tZSBpbnRlcnZhbCAkW2EsYl0kLCB3ZSBhcmUgYWN0dWFsbHkgbWVhc3VyaW5nIHRoZSBwcm9iYWJpbGl0eSBvZiBSViAkWCQgdG8gdGFrZSBhbnkgdmFsdWUgZnJvbSB0aGF0IGludGVydmFsLgoKT2JzZXJ2aW5nIHByb2JhYmlsaXRlcyBhcyBpbnRlZ3JhbHMsIGkuZS4gYXJlYXMgb2Ygc3VyZmFjZXMgdW5kZXIgdGhlIHAuZC5mIGN1cnZlIGlzIGNvbXBsZXRlbHkgaW4tc3luYyB3aXRoIEtvbG1vZ29yb3YgQXhpb21zIGZvciBwcm9iYWJpbGl0eS1hcy1hLW1lYXN1cmU6CgotIEJvdGggcHJvYmFiaWxpdHkgYW5kIGFyZWEgYXJlIG1lYXN1cmVzLCBhbmQgY2Fubm90IGJlIG5lZ2F0aXZlOwotIEFyZWEgb2YgYW4gZW1wdHkgc2V0LCBvciBhIHBvaW50L2xpbmUgaXMgemVyby4gU28gaXMgdGhlIHByb2JhYmlsaXR5OwotIEJpZ2dlciB0aGUgYXJlYSwgYmlnZ2VyIHRoZSBwcm9iYWJpbGl0eTsgYW5kIHdlIG1heSBlbmNvbXBhc3MgYmlnIGFyZWFzIGJ5IHRha2luZyBsb25nIGludGVydmFscyBvciBpbnRlcnZhbHMgYXJvdW5kIGhpZ2ggdmFsdWVzIG9mIHAuZC5mOwotIFdlIGNhbiBqdXN0IHN1bSBhcmVhcyBvZiBkaXNqb2ludCBmaWd1cmVzIHRvIGdldCB0aGUgdG90YWwgYXJlYTsgdGhlIHNhbWUgaXMgZm9yIHByb2JhYmlsaXRpZXMgb2YgZGlzam9pbnQgZXZlbnRzLgoKT25lIG1vcmUgaW1wb3J0YW50IHBvaW50LiBXZSBzYXcgdGhhdCBmb3IgZGlzY3JldGUgUlZzIGV2ZXJ5IHBvaW50IG1hdHRlcnMgKGV2ZW4gaWYgaXQncyBpbmZpbml0ZSkuIEZvciBzb21lIGRpc2NyZXRlIFJWICRYJCB3ZSBoYXZlCgokJFAoWCBcbGVxc2xhbnQgaykgXG5lcSBQKFggPCBrKS4kJAoKSG93ZXZlciwgdGhpcyBpcyBub3QgdGhlIGNhc2Ugd2l0aCBjb250aW51b3VzIFJWczsgdGhlcmUgb25lIHBvaW50IGRvZXNuJ3QgbWFrZSBhIGRpZmZlcmVuY2UgKHdoeT8pLiBTbywgZm9yIHNvbWUgY29udGludW91cyBSViAkWCQgd2UgaGF2ZQoKJCRQKGEgXGxlcXNsYW50IFggXGxlcXNsYW50IGIpID0gUChhIFxsZXFzbGFudCBYIDwgYikgPSBQKGEgPCBYIDwgYikuJCQKCi0tLQoKQXMgZGlzY3JldGUgUlZzIGhhdmUgYy5kLmYsIHNvIGNvbnRpbnVvdXMgUlZzIGhhdmUgb25lIGFzIHdlbGwuIEN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIGZvciBhIGNvbnRpbnVvdXMgcmFuZG9tIHZhcmlhYmxlIGlzIGdpdmVuIHdpdGggCgokJEYoYSkgPSBQKFhcbGVxc2xhbnQgYSkgPSBcaW50X3stXGluZnR5fV5hXHZhcnBoaSh4KWR4LiQkCgpDLmQuZiBvZiBhIGNvbnRpbnVvdXMgUlYgJFgkIGdpdmVzIHRoZSBwcm9iYWJpbGl0eSBvZiAkWCQgdGFraW5nIGFueSB2YWx1ZSBmcm9tIHRoZSBpbnRlcnZhbCAkKC1caW5mdHksIGFdJC4gT3IsIGFyZWEgdW5kZXIgdGhlIHAuZC5mIGN1cnZlIG9uIHRoYXQgaW50ZXJ2YWwuIEhlcmUncyBob3cgb25lIGMuZC5mLiBvZiBhIGNvbnRpbnVvdXMgUlYgbG9va3MgbGlrZToKCiFbXShfaW1nL2NkZi5wbmcpCkMuZC5mLiBpcyBhIHByZXR5IGhhbmR5IHRvb2wgZm9yIGNvbXB1dGluZyBwcm9iYWJpbGl0aWVzIG9mIGNvbnRpbnVvdXMgUlZzLCBldmVuIG1vcmUgdXNlZnVsIHRoYW4gcC5kLmYhIEluIGZhY3QsIGJ5IGtub3dpbmcgdGhlIGMuZC5mIHdlIGNhbiBjb21wdXRlIHByb2JhYmlsaXRlcyB3aGlsZSBhdm9pZGluZyBpbnRlZ3JhbHMsIHVzaW5nIHRoaXMgZm9ybXVsYToKCiQkUChhIFxsZXFzbGFudCBYIFxsZXFzbGFudCBiKSA9IEYoYikgLSBGKGEpLiQkCgpBbmQgd2UgY2FuIGFsc28gY2FsY3VsYXRlIAoKJCRQKFggXGdlcXNsYW50IGEpID0gMSAtIFAoWCA8IGEpID0gMSAtIEYoYSkuJCQKCi0tLQoKRm9yIGNvbnRpbnVvdXMgUlZzIHZlcnkgdXNlZnVsIGlzICpxdWFudGlsZSBmdW5jdGlvbiosIHdoaWNoIGlzIHRoZSBpbnZlcnNlIG9mIGEgYy5kLmY6CgokJFEocCkgPSBGXnstMX0ocCksXHFxdWFkIHBcaW4oMCwgMSkuJCQKClNpbXBseSBwdXQsIGZvciAkcCA9IDAuMSQgd2UgY2FuIGdldCBhIHZhbHVlICR4JCBmb3Igd2hpY2ggd2UgY2FuIGV4cGVjdCB0byBmaW5kIDEwJSBvZiBwb2ludHMgaW4gdGhlIGludGVydmFsICQoLVxpbmZ0eSwgeF0kIHdoZW4gc2FtcGxpbmcgZnJvbSBhIFJWICRYJCBoYXZpbmcgYy5kLmYgJEYkLiAKClRoZSBtb3N0IGltcG9ydGFudCB2YWx1ZXMgZm9yICRwJCBmb3IgdGhlIHF1YW50aWxlIGZ1bmN0aW9ucyBhcmUgMC4yNSwgMC41IGFuZCAwLjc1LCB3aGljaCBnaXZlIHZhbHVlcyBmb3IgcXVhcnRpbGVzIFExLCBRMiAobWVkaWFuKSBhbmQgUTMuIAoKIyMjIE5vcm1hbCAob3IgR2F1c3NpYW4pIGRpc3RyaWJ1dGlvbgoKTm9ybWFsIERpc3RyaWJ1dGlvbiwgZGVub3RlZCBieSAkXG1hdGhjYWx7Tn0oXG11LCBcc2lnbWFeMikkLCBoYXMgdHdvIHBhcmFtZXRlcnM6CgotICRcbXUkOiAqbWVhbiosIHdoaWNoIGRpY3RhdGVzIHRoZSBwZWFrIG9mIHRoZSBiZWxsLWN1cnZlLCBpLmUuIGEgcG9pbnQgd2hpY2ggbmVpZ2hib3VyaG9vZCBzaG91bGQgYmUgbW9zdCBkZW5zbHkgcG9wdWxhdGVkIGJ5IHNhbXBsZXM7CgotICRcc2lnbWEkOiAqc3RhbmRhcmQgZGV2aWF0aW9uKiwgd2hpY2ggZGljdGF0ZXMgdGhlIHRoaWNrbmVzcyBvZiB0aGUgYmVsbC1jdXJ2ZSwgaS5lLiB0aGUgZGlzcGVyc2lvbiBvZiB0aGUgc2FtcGxlIGF3YXkgZnJvbSB0aGUgbWVhbi4KCiRcc2lnbWFeMiQgd2hpY2ggYXBwZWFycyBpbiB0aGUgbm90YXRpb24gYWJvdmUgaXMgY2FsbGVkICp2YXJpYW5jZSouCgpUaGUgcC5kLmYgZm9yIFJWIHdpdGggTm9ybWFsIERpc3RyaWJ1dGlvbiAkWFxzaW1cbWF0aGNhbHtOfShcbXUsIFxzaWdtYV4yKSQgaXMgZ2l2ZW4gYnkKCiQkXHZhcnBoaSh4KSA9IFxmcmFjezF9e1xzaWdtYVxzcXJ0ezJccGl9fWVeey1cZnJhY3sxfXsyfVxiaWcoXGZyYWN7eC1cbXV9e1xzaWdtYX1cYmlnKV4yfS4kJAoKQSB2ZXJ5IHNwZWNpYWwgTm9ybWFsIERpc3RyaWJ1dGlvbiBpcyB0aGUgb25lIHdpdGggJFxtYXRoY2Fse059KDAsIDEpJCwgYW5kIGl0J3MgY2FsbGVkICpTdGFuZGFyZCBOb3JtYWwgRGlzdHJpYnV0aW9uKi4gSXRzIHAuZC5mLiBhbmQgYy5kLmYuIGFyZQoKJCRcdmFycGhpKHopID0gXGZyYWN7MX17XHNxcnR7MlxwaX19ZV57LVxmcmFje3peMn17Mn19JCQKCiQkXFBoaSh6KSA9IFxmcmFjezF9e1xzcXJ0ezJccGl9fVxpbnRfey1caW5mdHl9XnplXnstXGZyYWN7eF4yfXsyfX1keC4kJAoKSXQncyBpbnRlZ3JhbHMgYWdhaW4uLi4gc2hvdWxkbid0IGMuZC5mIHJlbGlldmUgdXMgb2YgY2FsY3VsYXRpbmcgaW50ZWdyYWxzPyBXZWxsLCB5ZXMsIGJ1dCB0aGVyZSdzIG5vIHNpbXBsZXIgd2F5IHRvIHJlcHJlc2VudCBjLmQuZiBmb3IgU3RhbmRhcmQgTm9ybWFsIERpc3RyaWJ1dGlvbi4gTHVja2lseSwgZXZlcnkgc3RhdGlzdGljcyBhbmQgcHJvYmFiaWxpdHkgdGV4dGJvb2sgaGFzIGEgdGFibGUgb2YgdmFsdWVzIG9mICRcUGhpKHopJCBmb3IgdmFyaW91cyB2YWx1ZXMgb2YgJHokLiAKCkxvb2tpbmcgYXQgb25lIG9mIHRob3NlIHRhYmxlcyB3ZSBvYnRhaW4sIGZvciAkWlxzaW1cbWF0aGNhbHtOfSgwLCAxKSQsIAoKJCRcUGhpKC0yLjI3KSA9IFAoWiA8IC0yLjI3KSA9IDAuMDExNi4kJAoKIyMjIyBFeGFtcGxlCgoqQXZlcmFnZSB0aW1lIGZvciBkZWxpdmVyeSBzZXJ2aWNlIHRvIGRlbGl2ZXIgZm9vZCBpcyAzMCBtaW51dGVzLCB3aXRoIHN0YW5kYXJkIGRldmlhbmNlIG9mIDEwIG1pbnV0ZXMuIEFzdW1taW5nIHRoZSB0aW1lIGZvciBkZWxpdmVyeSBpcyByYW5kb20gdmFyaWFibGUgd2l0aCBOb3JtYWwgRGlzdHJpYnV0aW9uLCBmaW5kIHByb2JhYmlsaXR5IG9mIHlvdXIgb3JkZXIgYXJyaXZpbmcgbW9yZSB0aGFuIGZpdmUgbWludXRlcyBsYXRlIHRoYW4gYXZlcmFnZS4qCgpXZSBoYXZlICRYXHNpbVxtYXRoY2Fse059KDMwLCAxMDApJCBhbmQgY2FsY3VsYXRlCgokJFAoWCA+IDM1KSA9IDEgLSBQKFhcbGVxc2xhbnQgMzUpLiQkCgpCdXQgd2FpdCwgd2UgZG9uJ3QgaGF2ZSBjLmQuZi4gZm9yIHRoaXMgTm9ybWFsIERpc3RyaWJ1dGlvbiwgYmVjYXVzZSBpdCdzIG5vdCBzdGFuZGFyZC4gV29ycnkgbm90LCB3ZSBjYW4gKnN0YW5kYXJkaXplKiBhIFJWICRYXHNpbVxtYXRoY2Fse059KFxtdSwgXHNpZ21hKSQgYnkgYXBwbHlpbmcgdGhlIGZvbGxvd2luZyB0cmFuc2Zvcm1hdGlvbgoKJCRaID0gXGZyYWN7WCAtIFxtdX17XHNpZ21hfS4kJAoKUlYgJFokIGhhcyB0aGUgU3RhbmRhcmQgTm9ybWFsIERpc3RyaWJ1dGlvbiwgYW5kIHdlIGNhbiBmaW5kZSB0aGUgdmFsdWUgb2YgaXRzIGMuZC5mLiBmb3JtIHRoZSB0YWJsZS4gU28sIHdlIGNvbnRpbnVlIG91ciBjYWxjdWxhdGlvbjoKCiQkUChYID4gMzUpID0gMSAtIFAoWFxsZXFzbGFudCAzNSkgPSAxIC0gUFxCaWcoXGZyYWN7WCAtIDMwfXsxMH1cbGVxc2xhbnQgIFxmcmFjezM1IC0gMzB9ezEwfVxCaWcpID0gMSAtIFAoWlxsZXFzbGFudCAwLjUpID0gMSAtIFxQaGkoMC41KSA9IDEgLSAwLjY5MTUgPSAwLjMwODUuJCQKCk5vdywgZm9yIHN0YXRpc3RpY2FsIGV4cGVyaW1lbnRzIHdpdGggY29udGludW91cyBvdXRjb21lcywgc3VjaCBhcyB0aG9zZSBtb2RlbGVkIGJ5IHRoZSBOb3JtYWwgRGlzdHJpYnV0aW9uLCB0aGUgdGhpbmdzIGFyZSBkaWZmZXJlbnQgdGhhbiB3aXRoIGRpc2NyZXRlIFIuVi5zLiBSZWNhbGwgdGhhdCBgUChYID09IHgpYCAtIHRoZSBwcm9iYWJpbGl0eSBvZiBzb21lICpleGFjdCosICpyZWFsKiB2YWx1ZSAtIGlzIGFsd2F5cyB6ZXJvLiBUaGF0IGlzIHNpbXBseSB0aGUgbmF0dXJlIG9mIHRoZSBjb250aW51dW0uIFdoYXQgd2UgY2FuIG9idGFpbiBmcm9tIGNvbnRpbnVvdXMgcHJvYmFiaWxpdHkgZnVuY3Rpb25zIGlzIGEgcHJvYmFiaWxpdHkgdGhhdCBzb21lIHZhbHVlIGZhbGxzIGluIHNvbWUgcHJlY2lzZWx5IGRlZmluZWQgaW50ZXJ2YWwuIElmIHdlIHdvdWxkIHdhbnQgdG8gZG8gdGhhdCBmcm9tIGEgUHJvYmFiaWxpdHkgRGVuc2l0eSBGdW5jdGlvbiwgd2Ugd291bGQgbmVlZCB0byAqaW50ZWdyYXRlKiB0aGF0IGZ1bmN0aW9uIGFjcm9zcyB0aGF0IGludGVydmFsLiBCdXQgdGhhdCBpcyBwcm9iYWJseSB3aGF0IHlvdSBkbyBub3Qgd2FudCB0byBkbywgYmVjYXVzZSB0aGVyZSBpcyBhIHdheSBzaW1wbGVyIGFwcHJvYWNoLCBpbGx1c3RyYXRlZCB3aXRoIGBwYmlub20oKWAgaW4gdGhlIGV4YW1wbGUgd2l0aCB0aGUgQmlub21pYWwgZXhwZXJpbWVudCBhYm92ZS4gQXNzdW1lIHRoYXQgd2UgYXJlIG9ic2VydmluZyBwZW9wbGUgaW4gYSBwb3B1bGF0aW9uIHdpdGggYW4gYXZlcmFnZSBoZWlnaHQgb2YgMTc0IGNtLCB3aXRoIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMTAgY20uIFdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IHRvIHJhbmRvbWx5IG1lZXQgYW55b25lIHdobyBpcyBsZXNzIHRoYW4gKG9yIGVxdWFsKSAxODAgY20gdGFsbD8KCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KcG5vcm0oMTgwLCBtZWFuID0gMTc0LCBzZCA9IDEwKQpgYGAKCkFuZCB3aGF0IGlzIHRoZSBwcm9iYWJpbGl0eSB0byByYW5kb21seSBvYnNlcnZlIGEgcGVyc29uIGJldHdlZW4gMTYwIGNtIGFuZCAxODAgY20/CgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CnBub3JtKDE4MCwgbWVhbiA9IDE3NCwgc2QgPSAxMCkgLSBwbm9ybSgxNjAsIG1lYW4gPSAxNzQsIHNkID0gMTApCmBgYAoKVGhhdCBpcyBob3cgeW91IG9idGFpbiB0aGUgcHJvYmFiaWxpdHkgb2YgcmVhbC12YWx1ZWQsIGNvbnRpbnVvdXMgc3RhdGlzdGljYWwgb3V0Y29tZXMuIExldCdzIHBsb3QgdGhlIFByb2JhYmlsaXR5IERlbnNpdHkgYW5kIHRoZSBDdW11bGF0aXZlIERpc3RyaWJ1dGlvbiBmdW5jdGlvbnMgZm9yIHRoaXMgc3RhdGlzdGljYWwgZXhwZXJpbWVudC4gRGVuc2l0eSBmaXJzdDoKCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0Kb2JzZXJ2YXRpb25zIDwtIDE6MzAwCm5vcm1hbFByb2JhYmlsaXR5IDwtIGRub3JtKG9ic2VydmF0aW9ucywgbWVhbiA9IDE3OSwgc2QgPSAxMCkKbm9ybWFsUHJvYmFiaWxpdHkKYGBgCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9Cm5vcm1hbFByb2JhYmlsaXR5IDwtIGRhdGEuZnJhbWUob2JzZXJ2YXRpb25zID0gb2JzZXJ2YXRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbnNpdHkgPSBub3JtYWxQcm9iYWJpbGl0eSkKZ2dwbG90KG5vcm1hbFByb2JhYmlsaXR5LCAKICAgICAgIGFlcyh4ID0gb2JzZXJ2YXRpb25zLCAKICAgICAgICAgICB5ID0gZGVuc2l0eSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIAogICAgICAgICAgIGZpbGwgPSAnZGFya3JlZCcsCiAgICAgICAgICAgY29sb3IgPSAnZGFya3JlZCcpICsKICBnZ3RpdGxlKCJHYXVzc2lhbiBEaXN0cmlidXRpb24iKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSwgc2l6ZSA9IDEwKSkKYGBgCgoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpvYnNlcnZhdGlvbnMgPC0gMTozMDAKbm9ybWFsUHJvYmFiaWxpdHkgPC0gcG5vcm0ob2JzZXJ2YXRpb25zLCBtZWFuID0gMTc0LCBzZCA9IDEwKQpzdW0obm9ybWFsUHJvYmFiaWxpdHkpCmBgYAoKYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpub3JtYWxQcm9iYWJpbGl0eSA8LSBkYXRhLmZyYW1lKG9ic2VydmF0aW9ucyA9IG9ic2VydmF0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5zaXR5ID0gbm9ybWFsUHJvYmFiaWxpdHkpCmdncGxvdChub3JtYWxQcm9iYWJpbGl0eSwgCiAgICAgICBhZXMoeCA9IG9ic2VydmF0aW9ucywgCiAgICAgICAgICAgeSA9IGRlbnNpdHkpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCAKICAgICAgICAgICBmaWxsID0gJ2RhcmtyZWQnLAogICAgICAgICAgIGNvbG9yID0gJ2RhcmtyZWQnKSArCiAgZ2d0aXRsZSgiR2F1c3NpYW4gQ3VtdWxhdGl2ZSBEaXN0cmlidXRpb24iKSArCiAgeWxhYigiUChoZWlnaHQgPD0geCkiKSArIAogIHRoZW1lX2J3KCkgKyAKICB0aGVtZShwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUsIHNpemUgPSAxMCkpCmBgYAoKSGVyZSBhcmUgc29tZSBleGFtcGxlcyBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpbiBuYXR1cmUgYW5kIHNvY2lldHk6CgotIEhlaWdodHMgb2YgSW5kaXZpZHVhbHM6IFRoZSBkaXN0cmlidXRpb24gb2YgaGVpZ2h0cyBpbiBhIHBvcHVsYXRpb24gb2Z0ZW4gZm9sbG93cyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSBtYWpvcml0eSBvZiBpbmRpdmlkdWFscyB0ZW5kIHRvIGNsdXN0ZXIgYXJvdW5kIHRoZSBtZWFuIGhlaWdodCwgd2l0aCBmZXdlciBpbmRpdmlkdWFscyBhdCBib3RoIGV4dHJlbWVzICh2ZXJ5IHRhbGwgb3IgdmVyeSBzaG9ydCkuCgotIElRIFNjb3JlczogSW50ZWxsaWdlbmNlIHF1b3RpZW50IChJUSkgc2NvcmVzIGFyZSBvZnRlbiBhcHByb3hpbWF0ZWQgYnkgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBJbiBhIGxhcmdlIHBvcHVsYXRpb24sIG1vc3QgaW5kaXZpZHVhbHMgZmFsbCBhcm91bmQgdGhlIGF2ZXJhZ2UgSVEgc2NvcmUsIHdpdGggZmV3ZXIgaW5kaXZpZHVhbHMgaGF2aW5nIHNjb3JlcyB0aGF0IGRldmlhdGUgc2lnbmlmaWNhbnRseSBmcm9tIHRoZSBtZWFuLgoKLSBCb2R5IE1hc3MgSW5kZXggKEJNSSk6IFRoZSBkaXN0cmlidXRpb24gb2YgQk1JIHNjb3Jlcywgd2hpY2ggcmVsYXRlcyB0byBib2R5IHdlaWdodCBhbmQgaGVpZ2h0LCB0ZW5kcyB0byBhcHByb3hpbWF0ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24uIEluIGEgZ2l2ZW4gcG9wdWxhdGlvbiwgbW9zdCBpbmRpdmlkdWFscyBoYXZlIEJNSSB2YWx1ZXMgbmVhciB0aGUgbWVhbiwgd2l0aCBmZXdlciBpbmRpdmlkdWFscyBoYXZpbmcgZXh0cmVtZWx5IGhpZ2ggb3IgbG93IEJNSSB2YWx1ZXMuCgotIFRlc3QgU2NvcmVzOiBJbiBlZHVjYXRpb25hbCBzZXR0aW5ncywgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0ZXN0IHNjb3JlcyBvZnRlbiBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gV2hlbiBhIGxhcmdlIG51bWJlciBvZiBzdHVkZW50cyB0YWtlIGEgdGVzdCwgdGhlaXIgc2NvcmVzIHRlbmQgdG8gZm9ybSBhIGJlbGwgY3VydmUsIHdpdGggbW9zdCBzY29yZXMgY2x1c3RlcmluZyBhcm91bmQgdGhlIGF2ZXJhZ2UgYW5kIGZld2VyIHNjb3JlcyBhdCB0aGUgZXh0cmVtZXMuCgotIEVycm9ycyBpbiBNZWFzdXJlbWVudHM6IEVycm9ycyBpbiBtZWFzdXJlbWVudCwgc3VjaCBhcyBpbiBzY2llbnRpZmljIGV4cGVyaW1lbnRzIG9yIHF1YWxpdHkgY29udHJvbCBwcm9jZXNzZXMsIG9mdGVuIGV4aGliaXQgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGlzIHBoZW5vbWVub24gaXMga25vd24gYXMgbWVhc3VyZW1lbnQgZXJyb3Igb3IgcmFuZG9tIGVycm9yLCB3aGVyZSB0aGUgZXJyb3JzIHRlbmQgdG8gY2VudGVyIGFyb3VuZCB6ZXJvIHdpdGggZGVjcmVhc2luZyBmcmVxdWVuY3kgYXMgdGhlIG1hZ25pdHVkZSBvZiB0aGUgZXJyb3IgaW5jcmVhc2VzLgoKLSBOYXR1cmFsIFBoZW5vbWVuYTogTWFueSBuYXR1cmFsIHBoZW5vbWVuYSwgc3VjaCBhcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHBhcnRpY2xlIHZlbG9jaXRpZXMgaW4gYSBnYXMsIHRoZSBzaXplcyBvZiByYWluZHJvcHMsIG9yIHRoZSBkaXN0cmlidXRpb24gb2YgcmVhY3Rpb24gdGltZXMgaW4gaHVtYW4gcGVyY2VwdGlvbiwgY2FuIGJlIG1vZGVsZWQgYnkgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24uCgotIEVycm9yIFRlcm1zIGluIFJlZ3Jlc3Npb24gQW5hbHlzaXM6IEluIHJlZ3Jlc3Npb24gYW5hbHlzaXMsIHRoZSBhc3N1bXB0aW9uIG9mIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGVycm9yIHRlcm1zIGlzIG9mdGVuIG1hZGUuIFRoaXMgYXNzdW1wdGlvbiBhbGxvd3MgZm9yIGVmZmljaWVudCBzdGF0aXN0aWNhbCBpbmZlcmVuY2UgYW5kIGh5cG90aGVzaXMgdGVzdGluZy4KCiMjIDYgVGhlIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc2FtcGxlIG1lYW4KCkxldCdzIGFzc3VtZSB0aGF0IHNvbWUgcXVhbnRpdHkgaW4gcmVhbGl0eSBmb2xsb3dzIGEgTm9ybWFsIERpc3RyaWJ1dGlvbiB3aXRoICRcbXUkID0gMTAwIGFuZCAkXHNpZ21hJCA9IDEyLCBhbmQgZHJhdyBtYW55LCBtYW55IHJhbmRvbSBzYW1wbGVzIG9mIHNpemUgYDEwMGAgZnJvbSB0aGlzIGRpc3RyaWJ1dGlvbjoKCmBgYHtyIGVjaG8gPSBUfQpuX3NhbXBsZXMgPC0gMTAwMDAwCnNhbXBsZU1lYW5zIDwtIHNhcHBseSgxOm5fc2FtcGxlcywKICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihybm9ybShuID0gMTAwLCBtZWFuID0gMTAwLCBzZCA9IDEyKSkKICAgICAgICAgICAgICAgICAgICAgICAgfSkKc2FtcGxlTWVhbnMgPC0gZGF0YS5mcmFtZShzYW1wbGVNZWFuID0gc2FtcGxlTWVhbnMpCmdncGxvdChzYW1wbGVNZWFucywgCiAgICAgICBhZXMoeCA9IHNhbXBsZU1lYW4pKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gLjEsIAogICAgICAgICAgICAgICAgIGZpbGwgPSAnd2hpdGUnLCAKICAgICAgICAgICAgICAgICBjb2xvciA9ICdwdXJwbGUnKSArCiAgZ2d0aXRsZSgiU2FtcGxpbmcgRGlzdHJpYnV0aW9uIG9mIHRoZSBTYW1wbGUgTWVhbiIpICsKICB4bGFiKCJTYW1wbGUgTWVhbiIpICsgCiAgdGhlbWVfYncoKSArIAogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSwgc2l6ZSA9IDEwKSkKYGBgCgpXaGF0IGlzIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYHNhbXBsZU1lYW5zYD8KCmBgYHtyIGVjaG8gPSBUfQpzZChzYW1wbGVNZWFucyRzYW1wbGVNZWFuKQpgYGAKCkFuZCByZWNhbGwgdGhhdCB0aGUgKipwb3B1bGF0aW9uIHN0YW5kYXJkIGRldmlhdGlvbioqIHdhcyBkZWZpbmVkIHRvIGJlICoqMTIqKi4gTm93OgoKYGBge3IgZWNobyA9IFR9CjEyL3NxcnQoMTAwKQpgYGAKCkFuZCB0aGlzIGlzIHJvdWdobHkgdGhlIHNhbWUsIHJpZ2h0PyAKClRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlICoqc2FtcGxpbmcgZGlzdHJpYnV0aW9uIG9mIGEgbWVhbioqIC0gYWxzbyBrbm93biBhcyB0aGUgKipzdGFuZGFyZCBlcnJvcioqIC0gaXMgZXF1YWwgdG8gdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgKipwb3B1bGF0aW9uKiogKGkuZS4gdGhlICp0cnVlIGRpc3RyaWJ1dGlvbiopIGRpdmlkZWQgYnkgJFxzcXJ0KE4pJDogCgokJFxzaWdtYV9cd2lkZXRpbGRle3h9ID0gXHNpZ21hL1xzcXJ0KE4pJCQKCmFuZCB0aGF0IG1lYW5zIHRoYXQgaWYgd2Uga25vdyB0aGUgKipzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uKioKCj4gU3RhbmRhcmQgZGV2aWF0aW9uIChTRCkgbWVhc3VyZXMgdGhlIGRpc3BlcnNpb24gb2YgYSBkYXRhc2V0IHJlbGF0aXZlIHRvIGl0cyBtZWFuLiBTdGFuZGFyZCBlcnJvciBvZiB0aGUgbWVhbiAoU0VNKSBtZWFzdXJlZCBob3cgbXVjaCBkaXNjcmVwYW5jeSB0aGVyZSBpcyBsaWtlbHkgdG8gYmUgaW4gYSBzYW1wbGUncyBtZWFuIGNvbXBhcmVkIHRvIHRoZSBwb3B1bGF0aW9uIG1lYW4uClRoZSBTRU0gdGFrZXMgdGhlIFNEIGFuZCBkaXZpZGVzIGl0IGJ5IHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgc2FtcGxlIHNpemUuIFNvdXJjZTogW0ludmVzdG9wZWRpYSwgU3RhbmRhcmQgRXJyb3Igb2YgdGhlIE1lYW4gdnMuIFN0YW5kYXJkIERldmlhdGlvbjogVGhlIERpZmZlcmVuY2VdKGh0dHBzOi8vd3d3LmludmVzdG9wZWRpYS5jb20vYXNrL2Fuc3dlcnMvMDQyNDE1L3doYXQtZGlmZmVyZW5jZS1iZXR3ZWVuLXN0YW5kYXJkLWVycm9yLW1lYW5zLWFuZC1zdGFuZGFyZC1kZXZpYXRpb24uYXNwKQoKIyMgNyBUaGUgQ2VudHJhbCBMaW1pdCBUaGVvcmVtCgpJbiB0aGUgZm9sbG93aW5nIHNldCBvZiBudW1lcmljYWwgc2ltdWxhdGlvbnMgd2Ugd2FudCB0byBkbyB0aGUgZm9sbG93aW5nOgoKLSBkZWZpbmUgYSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gYnkgcHJvdmlkaW5nIGEgc2V0IG9mIHBhcmFtZXRlcnMsIHNheSAkXG11JCBhbmQgJFxzaWdtYV4yJCBmb3IgdGhlIE5vcm1hbCwgb3IgJFxsYW1iZGEkIGZvciB0aGUgUG9pc3Nvbiwgb3IgKipuKiogYW5kICoqcCoqIGZvciB0aGUgQmlub21pYWw7Ci0gZWFjaCB0aW1lIHdlIHBpY2sgYSBkaXN0cmlidXRpb24sIHdlIHRha2UgCiAtIGEgcmFuZG9tIHNhbXBsZSBvZiBzaXplIGBzYW1wbGVOIDwtIDEwMGAsIAogLSBjb21wdXRlIHRoZSAqKm1lYW4qKiBvZiB0aGUgb2J0YWluZWQgcmFuZG9tIG51bWJlcnMsIGFuZCB0aGVuIAogLSB3ZSByZXBlYXQgdGhhdCBlaXRoZXIgMTAsIG9yIDEwMCwgb3IgMSwwMDAsIG9yIDEwLDAwMCB0aW1lcyAodmFyaWVzIGFzOiBgbWVhblNpemVzIDwtIGMoMTAsIDEwMCwgMTAwMCwgMTAwMDApYCkuCiAKTGV0J3Mgc2VlIHdoYXQgaGFwcGVuczoKCmBgYCB7ciBlY2hvPVR9CnNhbXBsZU4gPC0gMTAwCm1lYW5TaXplcyA8LSBjKDEwLCAxMDAsIDEwMDAsIDEwMDAwKQpgYGAKClBvaXNzb24gd2l0aCAkXGxhbWJkYSA9IDEkOgoKYGBgIHtyIGVjaG89VH0KIyAtIFBvaXNzb24gd2l0aCBsYW1iZGEgPSAxCmxhbWJkYSA9IDEKIyAtIFNldCBwbG90ICBwYXJhbWV0ZXJzCnBhcihtZnJvdyA9IGMoMiwgMikpCiMgLSBQbG90IQpmb3IgKG1lYW5TaXplIGluIG1lYW5TaXplcykgewogIHBvaXNNZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocnBvaXMoc2FtcGxlTiwgbGFtYmRhKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICApCiAgaGlzdChwb2lzTWVhbnMsIDUwLCBjb2w9InJlZCIsCiAgICAgICBtYWluID0gcGFzdGUwKCJOIHNhbXBsZXMgPSAiLCBtZWFuU2l6ZSksCiAgICAgICBjZXgubWFpbiA9IC43NSkKfQpgYGAKClBvaXNzb24gd2l0aCAkXGxhbWJkYSA9IDIkOgoKYGBgIHtyIGVjaG89VH0KIyAtIFBvaXNzb24gd2l0aCBsYW1iZGEgPSAyCmxhbWJkYSA9IDIKIyAtIFNldCBwbG90ICBwYXJhbWV0ZXJzCnBhcihtZnJvdyA9IGMoMiwgMikpCiMgLSBQbG90IQpmb3IgKG1lYW5TaXplIGluIG1lYW5TaXplcykgewogIHBvaXNNZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocnBvaXMoc2FtcGxlTiwgbGFtYmRhKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICApCiAgaGlzdChwb2lzTWVhbnMsIDUwLCBjb2w9InJlZCIsCiAgICAgICBtYWluID0gcGFzdGUwKCJOIHNhbXBsZXMgPSAiLCBtZWFuU2l6ZSksCiAgICAgICBjZXgubWFpbiA9IC43NSkKfQpgYGAKClBvaXNzb24gd2l0aCAkXGxhbWJkYSA9IDEwJDoKCmBgYCB7ciBlY2hvPVR9CiMgLSBQb2lzc29uIHdpdGggbGFtYmRhID0gMTAKbGFtYmRhID0gMTAKIyAtIFNldCBwbG90ICBwYXJhbWV0ZXJzCnBhcihtZnJvdyA9IGMoMiwgMikpCiMgLSBQbG90IQpmb3IgKG1lYW5TaXplIGluIG1lYW5TaXplcykgewogIHBvaXNNZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocnBvaXMoc2FtcGxlTiwgbGFtYmRhKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICApCiAgaGlzdChwb2lzTWVhbnMsIDUwLCBjb2w9InJlZCIsCiAgICAgICBtYWluID0gcGFzdGUwKCJOIHNhbXBsZXMgPSAiLCBtZWFuU2l6ZSksCiAgICAgICBjZXgubWFpbiA9IC43NSkKfQpgYGAKQmlub21pYWwgd2l0aCAqKnAqKiA9IC4xIGFuZCAqKm4qKiA9IDEwMDA6CgpgYGAge3IgZWNobz1UfQojIC0gQmlub21pYWwgd2l0aCBwID0gLjEgYW5kIG4gPSAxMDAwCnAgPSAuMQpuID0gMTAwMAojIC0gU2V0IHBsb3QgIHBhcmFtZXRlcnMKcGFyKG1mcm93ID0gYygyLCAyKSkKIyAtIFBsb3QhCmZvciAobWVhblNpemUgaW4gbWVhblNpemVzKSB7CiAgYmlub21NZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocmJpbm9tKG4gPSBzYW1wbGVOLCBzaXplID0gbiwgcHJvYiA9IHApKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgKQogIGhpc3QoYmlub21NZWFucywgNTAsIGNvbD0iZGFya29yYW5nZSIsCiAgICAgICBtYWluID0gcGFzdGUwKCJOIHNhbXBsZXMgPSAiLCBtZWFuU2l6ZSksCiAgICAgICBjZXgubWFpbiA9IC43NSkKfQpgYGAKCkJpbm9taWFsIHdpdGggKipwKiogPSAuNSBhbmQgKipuKiogPSAxMDAwOgoKYGBgIHtyIGVjaG89VH0KIyAtIEJpbm9taWFsIHdpdGggcCA9IC41IGFuZCBuID0gMTAwMApwID0gLjUKbiA9IDEwMAojIC0gU2V0IHBsb3QgIHBhcmFtZXRlcnMKcGFyKG1mcm93ID0gYygyLCAyKSkKIyAtIFBsb3QhCmZvciAobWVhblNpemUgaW4gbWVhblNpemVzKSB7CiAgYmlub21NZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocmJpbm9tKG4gPSBzYW1wbGVOLCBzaXplID0gbiwgcHJvYiA9IHApKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICkKICBoaXN0KGJpbm9tTWVhbnMsIDUwLCBjb2w9ImRhcmtvcmFuZ2UiLAogICAgICAgbWFpbiA9IHBhc3RlMCgiTiBzYW1wbGVzID0gIiwgbWVhblNpemUpLAogICAgICAgY2V4Lm1haW4gPSAuNzUpCn0KYGBgCk5vcm1hbCB3aXRoICRcbXUgPSAxMCQgYW5kICRcc2lnbWEgPSAxLjUkOgoKYGBgIHtyIGVjaG89VH0KIyAtIE5vcm1hbCB3aXRoIG1lYW4gPSAxMCBhbmQgc2QgPSAxLjUKbWVhbiA9IDEwCnNkID0gMS41CiMgLSBTZXQgcGxvdCAgcGFyYW1ldGVycwpwYXIobWZyb3cgPSBjKDIsIDIpKQojIC0gUGxvdCEKZm9yIChtZWFuU2l6ZSBpbiBtZWFuU2l6ZXMpIHsKICBub3JtYWxNZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocm5vcm0oc2FtcGxlTiwgbWVhbiA9IG1lYW4sIHNkID0gc2QpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICkKICBoaXN0KG5vcm1hbE1lYW5zLCA1MCwgY29sPSJsaWdodGJsdWUiLAogICAgICAgbWFpbiA9IHBhc3RlMCgiTiBzYW1wbGVzID0gIiwgbWVhblNpemUpLAogICAgICAgY2V4Lm1haW4gPSAuNzUpCn0KYGBgCk5vcm1hbCB3aXRoICRcbXUgPSAxNzUkIGFuZCAkXHNpZ21hID0gMTEuNCQ6CgpgYGAge3IgZWNobz1UfQojIC0gTm9ybWFsIHdpdGggbWVhbiA9IDE3NSBhbmQgc2QgPSAxMS40Cm1lYW4gPSAxNzUKc2QgPSAxMS40CiMgLSBTZXQgcGxvdCAgcGFyYW1ldGVycwpwYXIobWZyb3cgPSBjKDIsIDIpKQojIC0gUGxvdCEKZm9yIChtZWFuU2l6ZSBpbiBtZWFuU2l6ZXMpIHsKICBub3JtYWxNZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocm5vcm0oc2FtcGxlTiwgbWVhbiA9IG1lYW4sIHNkID0gc2QpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICkKICBoaXN0KG5vcm1hbE1lYW5zLCA1MCwgY29sPSJsaWdodGJsdWUiLAogICAgICAgbWFpbiA9IHBhc3RlMCgiTiBzYW1wbGVzID0gIiwgbWVhblNpemUpLAogICAgICAgY2V4Lm1haW4gPSAuNzUpCn0KYGBgCk5vcm1hbCB3aXRoICRcbXUgPSAxMDAkIGFuZCAkXHNpZ21hID0gMjUkOgoKYGBgIHtyIGVjaG89VH0KIyAtIE5vcm1hbCB3aXRoIG1lYW4gPSAxMDAgYW5kIHNkID0gMjUKbWVhbiA9IDEwMApzZCA9IDI1CiMgLSBTZXQgcGxvdCAgcGFyYW1ldGVycwpwYXIobWZyb3cgPSBjKDIsIDIpKQojIC0gUGxvdCEKZm9yIChtZWFuU2l6ZSBpbiBtZWFuU2l6ZXMpIHsKICBub3JtYWxNZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocm5vcm0oc2FtcGxlTiwgbWVhbiA9IG1lYW4sIHNkID0gc2QpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICkKICBoaXN0KG5vcm1hbE1lYW5zLCA1MCwgY29sPSJsaWdodGJsdWUiLAogICAgICAgbWFpbiA9IHBhc3RlMCgiTiBzYW1wbGVzID0gIiwgbWVhblNpemUpLAogICAgICAgY2V4Lm1haW4gPSAuNzUpCn0KYGBgCgpXaGF0IGhhcHBlbnMgaWYgd2Ugc3RhcnQgaW5jcmVhc2luZyBgc2FtcGxlTmAgYW5kIGtlZXAgdGhlIG51bWJlciBvZiBzYW1wbGVzIGF0IHNvbWUgZGVjZW50IG51bWJlciwgc2F5IGBtZWFuU2l6ZSA9IDEwMDAwYD8KCmBgYCB7ciBlY2hvPVR9CnNhbXBsZU4gPC0gYygxMCwgMTAwLCAxMDAwLCAxMDAwMDApCm1lYW5TaXplIDwtIGMoMTAwMDApCmBgYAoKUG9pc3NvbiB3aXRoICRcbGFtYmRhID0gMyQ6CgpgYGAge3IgZWNobz1UfQojIC0gUG9pc3NvbiB3aXRoIGxhbWJkYSA9IDEKbGFtYmRhID0gMwojIC0gU2V0IHBsb3QgIHBhcmFtZXRlcnMKcGFyKG1mcm93ID0gYygyLCAyKSkKIyAtIFBsb3QhCmZvciAoc2FtcGxlTiBpbiBzYW1wbGVOKSB7CiAgcG9pc01lYW5zIDwtIHNhcHBseSgxOm1lYW5TaXplLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihycG9pcyhzYW1wbGVOLCBsYW1iZGEpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICkKICBoaXN0KHBvaXNNZWFucywgNTAsIGNvbD0icmVkIiwKICAgICAgIG1haW4gPSBwYXN0ZTAoIlNhbXBsZSBOID0gIiwgc2FtcGxlTiksCiAgICAgICBjZXgubWFpbiA9IC43NSkKfQpgYGAKCk5vcm1hbCB3aXRoICRcbXUgPSAxMCQgYW5kICRcc2lnbWEgPSAuNzUkOgoKYGBgIHtyIGVjaG89VH0Kc2FtcGxlTiA8LSBjKDEwLCAxMDAsIDEwMDAsIDEwMDAwMCkKbWVhblNpemUgPC0gYygxMDAwMCkKYGBgCgpgYGAge3IgZWNobz1UfQojIC0gTm9ybWFsIHdpdGggbWVhbiA9IDEwIGFuZCBzZCA9IC43NQptZWFuID0gMTAKc2QgPSAuNzUKIyAtIFNldCBwbG90ICBwYXJhbWV0ZXJzCnBhcihtZnJvdyA9IGMoMiwgMikpCiMgLSBQbG90IQpmb3IgKHNhbXBsZU4gaW4gc2FtcGxlTikgewogIG5vcm1NZWFucyA8LSBzYXBwbHkoMTptZWFuU2l6ZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4ocm5vcm0oc2FtcGxlTiwgbWVhbiA9IG1lYW4sIHNkID0gc2QpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICkKICBoaXN0KG5vcm1NZWFucywgNTAsIGNvbD0ibGlnaHRibHVlIiwKICAgICAgIG1haW4gPSBwYXN0ZTAoIlNhbXBsZSBOID0gIiwgc2FtcGxlTiksCiAgICAgICBjZXgubWFpbiA9IC43NSkKfQpgYGAKCkFsbCB0aGUgc2FtZSBhcyB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgaW5jcmVhc2U/ICoqRXhhY3RseSoqOgoKPiBDZW50cmFsIExpbWl0IFRoZW9yZW0uIEluIHByb2JhYmlsaXR5IHRoZW9yeSwgdGhlIGNlbnRyYWwgbGltaXQgdGhlb3JlbSAoQ0xUKSBlc3RhYmxpc2hlcyB0aGF0LCBpbiBtYW55IHNpdHVhdGlvbnMsIHdoZW4gaW5kZXBlbmRlbnQgcmFuZG9tIHZhcmlhYmxlcyBhcmUgYWRkZWQsIHRoZWlyIHByb3Blcmx5IG5vcm1hbGl6ZWQgc3VtIHRlbmRzIHRvd2FyZCBhIG5vcm1hbCBkaXN0cmlidXRpb24gKGluZm9ybWFsbHkgYSBiZWxsIGN1cnZlKSBldmVuIGlmIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMgdGhlbXNlbHZlcyBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiBUaGUgdGhlb3JlbSBpcyBhIGtleSBjb25jZXB0IGluIHByb2JhYmlsaXR5IHRoZW9yeSBiZWNhdXNlIGl0IGltcGxpZXMgdGhhdCBwcm9iYWJpbGlzdGljIGFuZCBzdGF0aXN0aWNhbCBtZXRob2RzIHRoYXQgd29yayBmb3Igbm9ybWFsIGRpc3RyaWJ1dGlvbnMgY2FuIGJlIGFwcGxpY2FibGUgdG8gbWFueSBwcm9ibGVtcyBpbnZvbHZpbmcgb3RoZXIgdHlwZXMgb2YgZGlzdHJpYnV0aW9ucy4gU291cmNlOiBbQ2VudHJhbCBMaW1pdCBUaGVvcmVtLCBFbmdsaXNoIFdpa2lwZWRpYSwgYWNjZXNzZWQ6IDIwMjEvMDIvMTddKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NlbnRyYWxfbGltaXRfdGhlb3JlbSkKCkRvZXMgaXQgKiphbHdheXMgd29yayoqPyBTdHJpY3RseTogTk8uIEVudGVyIFtDYXVjaHkgRGlzdHJpYnV0aW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9DYXVjaHlfZGlzdHJpYnV0aW9uKToKCmBgYCB7ciBlY2hvPVR9CiMgLSBDYXVjaHkgd2l0aCBsb2NhdGlvbiA9IDAsIHNjYWxlID0gMQojIC0gc2V0IHBsb3QgIHBhcmFtZXRlcnMKcGFyKG1mcm93PWMoMiwyKSkKIyAtIHBsb3QhCmZvciAobWVhblNpemUgaW4gYygxMDAsIDEwMDAsIDEwMDAwLCAxMDAwMDApKSB7CiAgY2F1Y2h5U3VtcyA8LSB1bmxpc3QobGFwcGx5KHNlcSgxOm1lYW5TaXplKSwgZnVuY3Rpb24oeCkgewogICAgc3VtKHJjYXVjaHkoMTAwMCwgbG9jYXRpb24gPSAwLCBzY2FsZSA9IDEpKQogIH0pKQogIGhpc3QoY2F1Y2h5U3VtcywgNTAsIGNvbD0ib3JhbmdlIiwKICAgICAgIG1haW4gPSBwYXN0ZSgiTiBzYW1wbGVzID0gIiwgbWVhblNpemUsIHNlcD0iIiksCiAgICAgICBjZXgubWFpbiA9IC43NSkKfQpgYGAKCi4uLiBhbHNvIGtub3duIGFzIFtUaGUgV2l0Y2ggb2YgQWduZXNpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9XaXRjaF9vZl9BZ25lc2kpLi4uIAoKKioqCgpBbGVrc2FuZGFyIEN2ZXRrb3ZpxIcKCkdvcmFuIFMuIE1pbG92YW5vdmnEhwoKRGF0YUtvbGVrdGl2LCAyMDIzLgoKY29udGFjdDogZ29yYW4ubWlsb3Zhbm92aWNAZGF0YWtvbGVrdGl2LmNvbQoKIVtdKF9pbWcvREtfTG9nb18xMDAucG5nKQoKKioqCkxpY2Vuc2U6IFtHUEx2M10oaHR0cDovL3d3dy5nbnUub3JnL2xpY2Vuc2VzL2dwbC0zLjAudHh0KQpUaGlzIE5vdGVib29rIGlzIGZyZWUgc29mdHdhcmU6IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkgaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnkgdGhlIEZyZWUgU29mdHdhcmUgRm91bmRhdGlvbiwgZWl0aGVyIHZlcnNpb24gMyBvZiB0aGUgTGljZW5zZSwgb3IgKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4KVGhpcyBOb3RlYm9vayBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLCBidXQgV0lUSE9VVCBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZiBNRVJDSEFOVEFCSUxJVFkgb3IgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UuICBTZWUgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGZvciBtb3JlIGRldGFpbHMuCllvdSBzaG91bGQgaGF2ZSByZWNlaXZlZCBhIGNvcHkgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFsb25nIHdpdGggdGhpcyBOb3RlYm9vay4gSWYgbm90LCBzZWUgPGh0dHA6Ly93d3cuZ251Lm9yZy9saWNlbnNlcy8+LgoKKioqCgoK